[CLEANUP] Alwas put null at the last position
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Http / ServerRequest.php
1 <?php
2 namespace TYPO3\CMS\Core\Http;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use Psr\Http\Message\ServerRequestInterface;
18 use Psr\Http\Message\StreamInterface;
19 use Psr\Http\Message\UploadedFileInterface;
20
21 /**
22 * Represents a typical request incoming from the server to be processed
23 * by the TYPO3 Core. The original request is built from the ServerRequestFactory
24 * inside TYPO3's Bootstrap.
25 *
26 * Note that the PSR-7 standard works with immutable value objects, meaning that
27 * any modification to a Request object using the "with" methods will result
28 * in a new Request object.
29 *
30 * Highly inspired by https://github.com/phly/http/
31 *
32 * @internal Note that this is not public API yet.
33 */
34 class ServerRequest extends Request implements ServerRequestInterface
35 {
36 /**
37 * @var array
38 */
39 protected $attributes = [];
40
41 /**
42 * @var array
43 */
44 protected $cookieParams = [];
45
46 /**
47 * @var array
48 */
49 protected $parsedBody;
50
51 /**
52 * @var array
53 */
54 protected $queryParams = [];
55
56 /**
57 * @var array
58 */
59 protected $serverParams = [];
60
61 /**
62 * @var array
63 */
64 protected $uploadedFiles = [];
65
66 /**
67 * Constructor, the only place to set all parameters of this Message/Request
68 *
69 * @param string|null $uri URI for the request, if any.
70 * @param string|null $method HTTP method for the request, if any.
71 * @param string|resource|StreamInterface $body Message body, if any.
72 * @param array $headers Headers for the message, if any.
73 * @param array $serverParams Server parameters, typically from $_SERVER
74 * @param array $uploadedFiles Upload file information, a tree of UploadedFiles
75 * @throws \InvalidArgumentException for any invalid value.
76 */
77 public function __construct($uri = null, $method = null, $body = 'php://input', array $headers = [], array $serverParams = [], array $uploadedFiles = null)
78 {
79 if ($uploadedFiles !== null) {
80 $this->validateUploadedFiles($uploadedFiles);
81 }
82
83 parent::__construct($uri, $method, $body, $headers);
84
85 $this->serverParams = $serverParams;
86 $this->uploadedFiles = $uploadedFiles;
87 }
88
89 /**
90 * Retrieve server parameters.
91 *
92 * Retrieves data related to the incoming request environment,
93 * typically derived from PHP's $_SERVER superglobal. The data IS NOT
94 * REQUIRED to originate from $_SERVER.
95 *
96 * @return array
97 */
98 public function getServerParams()
99 {
100 return $this->serverParams;
101 }
102
103 /**
104 * Retrieve cookies.
105 *
106 * Retrieves cookies sent by the client to the server.
107 *
108 * The data MUST be compatible with the structure of the $_COOKIE
109 * superglobal.
110 *
111 * @return array
112 */
113 public function getCookieParams()
114 {
115 return $this->cookieParams;
116 }
117
118 /**
119 * Return an instance with the specified cookies.
120 *
121 * The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
122 * be compatible with the structure of $_COOKIE. Typically, this data will
123 * be injected at instantiation.
124 *
125 * This method MUST NOT update the related Cookie header of the request
126 * instance, nor related values in the server params.
127 *
128 * This method MUST be implemented in such a way as to retain the
129 * immutability of the message, and MUST return an instance that has the
130 * updated cookie values.
131 *
132 * @param array $cookies Array of key/value pairs representing cookies.
133 * @return ServerRequest
134 */
135 public function withCookieParams(array $cookies)
136 {
137 $clonedObject = clone $this;
138 $clonedObject->cookieParams = $cookies;
139 return $clonedObject;
140 }
141
142 /**
143 * Retrieve query string arguments.
144 *
145 * Retrieves the deserialized query string arguments, if any.
146 *
147 * Note: the query params might not be in sync with the URI or server
148 * params. If you need to ensure you are only getting the original
149 * values, you may need to parse the query string from `getUri()->getQuery()`
150 * or from the `QUERY_STRING` server param.
151 *
152 * @return array
153 */
154 public function getQueryParams()
155 {
156 return $this->queryParams;
157 }
158
159 /**
160 * Return an instance with the specified query string arguments.
161 *
162 * These values SHOULD remain immutable over the course of the incoming
163 * request. They MAY be injected during instantiation, such as from PHP's
164 * $_GET superglobal, or MAY be derived from some other value such as the
165 * URI. In cases where the arguments are parsed from the URI, the data
166 * MUST be compatible with what PHP's parse_str() would return for
167 * purposes of how duplicate query parameters are handled, and how nested
168 * sets are handled.
169 *
170 * Setting query string arguments MUST NOT change the URI stored by the
171 * request, nor the values in the server params.
172 *
173 * This method MUST be implemented in such a way as to retain the
174 * immutability of the message, and MUST return an instance that has the
175 * updated query string arguments.
176 *
177 * @param array $query Array of query string arguments, typically from
178 * $_GET.
179 * @return ServerRequest
180 */
181 public function withQueryParams(array $query)
182 {
183 $clonedObject = clone $this;
184 $clonedObject->queryParams = $query;
185 return $clonedObject;
186 }
187
188 /**
189 * Retrieve normalized file upload data.
190 *
191 * This method returns upload metadata in a normalized tree, with each leaf
192 * an instance of Psr\Http\Message\UploadedFileInterface.
193 *
194 * These values MAY be prepared from $_FILES or the message body during
195 * instantiation, or MAY be injected via withUploadedFiles().
196 *
197 * @return array An array tree of UploadedFileInterface instances; an empty
198 * array MUST be returned if no data is present.
199 */
200 public function getUploadedFiles()
201 {
202 return $this->uploadedFiles;
203 }
204
205 /**
206 * Create a new instance with the specified uploaded files.
207 *
208 * This method MUST be implemented in such a way as to retain the
209 * immutability of the message, and MUST return an instance that has the
210 * updated body parameters.
211 *
212 * @param array $uploadedFiles An array tree of UploadedFileInterface instances.
213 * @return ServerRequest
214 * @throws \InvalidArgumentException if an invalid structure is provided.
215 */
216 public function withUploadedFiles(array $uploadedFiles)
217 {
218 $this->validateUploadedFiles($uploadedFiles);
219 $clonedObject = clone $this;
220 $clonedObject->uploadedFiles = $uploadedFiles;
221 return $clonedObject;
222 }
223
224 /**
225 * Retrieve any parameters provided in the request body.
226 *
227 * If the request Content-Type is either application/x-www-form-urlencoded
228 * or multipart/form-data, and the request method is POST, this method MUST
229 * return the contents of $_POST.
230 *
231 * Otherwise, this method may return any results of deserializing
232 * the request body content; as parsing returns structured content, the
233 * potential types MUST be arrays or objects only. A null value indicates
234 * the absence of body content.
235 *
236 * @return array|object|null The deserialized body parameters, if any.
237 * These will typically be an array or object.
238 */
239 public function getParsedBody()
240 {
241 return $this->parsedBody;
242 }
243
244 /**
245 * Return an instance with the specified body parameters.
246 *
247 * These MAY be injected during instantiation.
248 *
249 * If the request Content-Type is either application/x-www-form-urlencoded
250 * or multipart/form-data, and the request method is POST, use this method
251 * ONLY to inject the contents of $_POST.
252 *
253 * The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
254 * deserializing the request body content. Deserialization/parsing returns
255 * structured data, and, as such, this method ONLY accepts arrays or objects,
256 * or a null value if nothing was available to parse.
257 *
258 * As an example, if content negotiation determines that the request data
259 * is a JSON payload, this method could be used to create a request
260 * instance with the deserialized parameters.
261 *
262 * This method MUST be implemented in such a way as to retain the
263 * immutability of the message, and MUST return an instance that has the
264 * updated body parameters.
265 *
266 * @param array|object|null $data The deserialized body data. This will
267 * typically be in an array or object.
268 * @return ServerRequest
269 * @throws \InvalidArgumentException if an unsupported argument type is
270 * provided.
271 */
272 public function withParsedBody($data)
273 {
274 $clonedObject = clone $this;
275 $clonedObject->parsedBody = $data;
276 return $clonedObject;
277 }
278
279 /**
280 * Retrieve attributes derived from the request.
281 *
282 * The request "attributes" may be used to allow injection of any
283 * parameters derived from the request: e.g., the results of path
284 * match operations; the results of decrypting cookies; the results of
285 * deserializing non-form-encoded message bodies; etc. Attributes
286 * will be application and request specific, and CAN be mutable.
287 *
288 * @return array Attributes derived from the request.
289 */
290 public function getAttributes()
291 {
292 return $this->attributes;
293 }
294
295 /**
296 * Retrieve a single derived request attribute.
297 *
298 * Retrieves a single derived request attribute as described in
299 * getAttributes(). If the attribute has not been previously set, returns
300 * the default value as provided.
301 *
302 * This method obviates the need for a hasAttribute() method, as it allows
303 * specifying a default value to return if the attribute is not found.
304 *
305 * @see getAttributes()
306 *
307 * @param string $name The attribute name.
308 * @param mixed $default Default value to return if the attribute does not exist.
309 * @return mixed
310 */
311 public function getAttribute($name, $default = null)
312 {
313 return isset($this->attributes[$name]) ? $this->attributes[$name] : $default;
314 }
315
316 /**
317 * Return an instance with the specified derived request attribute.
318 *
319 * This method allows setting a single derived request attribute as
320 * described in getAttributes().
321 *
322 * This method MUST be implemented in such a way as to retain the
323 * immutability of the message, and MUST return an instance that has the
324 * updated attribute.
325 *
326 * @see getAttributes()
327 *
328 * @param string $name The attribute name.
329 * @param mixed $value The value of the attribute.
330 * @return ServerRequest
331 */
332 public function withAttribute($name, $value)
333 {
334 $clonedObject = clone $this;
335 $clonedObject->attributes[$name] = $value;
336 return $clonedObject;
337 }
338
339 /**
340 * Return an instance that removes the specified derived request attribute.
341 *
342 * This method allows removing a single derived request attribute as
343 * described in getAttributes().
344 *
345 * This method MUST be implemented in such a way as to retain the
346 * immutability of the message, and MUST return an instance that removes
347 * the attribute.
348 *
349 * @see getAttributes()
350 *
351 * @param string $name The attribute name.
352 * @return ServerRequest
353 */
354 public function withoutAttribute($name)
355 {
356 $clonedObject = clone $this;
357 if (!isset($clonedObject->attributes[$name])) {
358 return $clonedObject;
359 }
360 unset($clonedObject->attributes[$name]);
361 return $clonedObject;
362 }
363
364 /**
365 * Recursively validate the structure in an uploaded files array.
366 *
367 * @param array $uploadedFiles
368 * @throws \InvalidArgumentException if any leaf is not an UploadedFileInterface instance.
369 */
370 protected function validateUploadedFiles(array $uploadedFiles)
371 {
372 foreach ($uploadedFiles as $file) {
373 if (is_array($file)) {
374 $this->validateUploadedFiles($file);
375 continue;
376 }
377 if (!$file instanceof UploadedFileInterface) {
378 throw new \InvalidArgumentException('Invalid file in uploaded files structure.', 1436717281);
379 }
380 }
381 }
382 }