1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of slick/http |
5
|
|
|
* |
6
|
|
|
* For the full copyright and license information, please view the LICENSE |
7
|
|
|
* file that was distributed with this source code. |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
namespace Slick\Http\Message\Server; |
11
|
|
|
|
12
|
|
|
use Psr\Http\Message\ServerRequestInterface; |
13
|
|
|
use Psr\Http\Message\StreamInterface; |
14
|
|
|
use Psr\Http\Message\UriInterface; |
15
|
|
|
use Slick\Http\Message\Exception\InvalidArgumentException; |
16
|
|
|
use Slick\Http\Message\Request as HttpRequest; |
17
|
|
|
use Slick\Http\Message\Stream\TextStream; |
18
|
|
|
use Slick\Http\Message\Uri; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Request |
22
|
|
|
* |
23
|
|
|
* @package Slick\Http\Message\Server |
24
|
|
|
*/ |
25
|
|
|
class Request extends HttpRequest implements ServerRequestInterface |
26
|
|
|
{ |
27
|
|
|
|
28
|
|
|
/** |
29
|
|
|
* @var array |
30
|
|
|
*/ |
31
|
|
|
private $server; |
32
|
|
|
|
33
|
|
|
/** |
34
|
|
|
* @var array |
35
|
|
|
*/ |
36
|
|
|
private $cookies; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* @var array |
40
|
|
|
*/ |
41
|
|
|
private $queryParams; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var UploadedFile[] |
45
|
|
|
*/ |
46
|
|
|
private $uploadedFiles; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var mixed |
50
|
|
|
*/ |
51
|
|
|
private $parsedBody; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var array |
55
|
|
|
*/ |
56
|
|
|
private $attributes = []; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Creates an HTTP Server Request Message |
60
|
|
|
* |
61
|
|
|
* @param string $method |
62
|
|
|
* @param string|StreamInterface $body |
63
|
|
|
* @param null|string|UriInterface $target |
64
|
|
|
* @param array $headers |
65
|
|
|
*/ |
66
|
|
|
public function __construct($method = null, $target = null, $body = null, array $headers = []) |
67
|
|
|
{ |
68
|
|
|
$method = null === $method |
69
|
|
|
? $this->getServerParams()['REQUEST_METHOD'] |
70
|
|
|
: $method; |
71
|
|
|
|
72
|
|
|
$body = null === $body |
73
|
|
|
? $this->getPhpInputStream() |
74
|
|
|
: $body; |
75
|
|
|
|
76
|
|
|
parent::__construct($method, $target, $body, $headers); |
77
|
|
|
$this->loadHeaders(); |
78
|
|
|
|
79
|
|
|
$this->setUri(RequestUriFactory::create($this)); |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
/** |
83
|
|
|
* Retrieve server parameters. |
84
|
|
|
* |
85
|
|
|
* Retrieves data related to the incoming request environment, |
86
|
|
|
* typically derived from PHP's $_SERVER super-global. |
87
|
|
|
* |
88
|
|
|
* @return array |
89
|
|
|
*/ |
90
|
|
|
public function getServerParams() |
91
|
|
|
{ |
92
|
|
|
if (! $this->server) { |
|
|
|
|
93
|
|
|
$this->server = $_SERVER; |
94
|
|
|
} |
95
|
|
|
return $this->server; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Retrieve cookies. |
100
|
|
|
* |
101
|
|
|
* Retrieves cookies sent by the client to the server. |
102
|
|
|
* |
103
|
|
|
* @return array |
104
|
|
|
*/ |
105
|
|
|
public function getCookieParams() |
106
|
|
|
{ |
107
|
|
|
if (! $this->cookies) { |
|
|
|
|
108
|
|
|
$this->cookies = $_COOKIE; |
109
|
|
|
} |
110
|
|
|
return $this->cookies; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Return an instance with the specified cookies. |
115
|
|
|
* |
116
|
|
|
* @param array $cookies Array of key/value pairs representing cookies. |
117
|
|
|
* @return Request |
118
|
|
|
*/ |
119
|
|
|
public function withCookieParams(array $cookies) |
120
|
|
|
{ |
121
|
|
|
$request = clone $this; |
122
|
|
|
$request->cookies = $cookies; |
123
|
|
|
return $request; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
/** |
127
|
|
|
* Retrieve query string arguments. |
128
|
|
|
* |
129
|
|
|
* Retrieves the deserialized query string arguments, if any. |
130
|
|
|
* |
131
|
|
|
* @return array |
132
|
|
|
*/ |
133
|
|
|
public function getQueryParams() |
134
|
|
|
{ |
135
|
|
|
if (! $this->queryParams) { |
|
|
|
|
136
|
|
|
$this->queryParams = $this->detectQueryParams(); |
137
|
|
|
} |
138
|
|
|
return $this->queryParams; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* Return an instance with the specified query string arguments. |
143
|
|
|
* |
144
|
|
|
* @param array $query Array of query string arguments, typically from |
145
|
|
|
* $_GET. |
146
|
|
|
* |
147
|
|
|
* @return Request |
148
|
|
|
*/ |
149
|
|
|
public function withQueryParams(array $query) |
150
|
|
|
{ |
151
|
|
|
$request = clone $this; |
152
|
|
|
$request->queryParams = $query; |
153
|
|
|
return $request; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
/** |
157
|
|
|
* Retrieve normalized file upload data. |
158
|
|
|
* |
159
|
|
|
* @return UploadedFile[] |
160
|
|
|
*/ |
161
|
|
|
public function getUploadedFiles() |
162
|
|
|
{ |
163
|
|
|
if (null === $this->uploadedFiles) { |
164
|
|
|
$this->uploadedFiles = UploadedFilesFactory::createFiles(); |
165
|
|
|
} |
166
|
|
|
return $this->uploadedFiles; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* Create a new instance with the specified uploaded files. |
171
|
|
|
* |
172
|
|
|
* @param array $uploadedFiles An array tree of UploadedFileInterface instances. |
173
|
|
|
* @return Request |
174
|
|
|
* |
175
|
|
|
* @throws InvalidArgumentException if an invalid structure is provided. |
176
|
|
|
*/ |
177
|
|
|
public function withUploadedFiles(array $uploadedFiles) |
178
|
|
|
{ |
179
|
|
|
if (! $this->checkUploadedFiles($uploadedFiles)) { |
180
|
|
|
throw new InvalidArgumentException( |
181
|
|
|
"The uploaded files array given has at least one leaf that is not ". |
182
|
|
|
"an UploadedFile object." |
183
|
|
|
); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
$request = clone $this; |
187
|
|
|
$request->uploadedFiles = $uploadedFiles; |
188
|
|
|
return $request; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Detects the query params from server and/or request URI |
194
|
|
|
* |
195
|
|
|
* @return array |
196
|
|
|
*/ |
197
|
|
|
private function detectQueryParams() |
198
|
|
|
{ |
199
|
|
|
$uri = new Uri('http://example.org'.$this->getRequestTarget()); |
200
|
|
|
parse_str($uri->getQuery(), $params); |
201
|
|
|
return array_merge($_GET, $params); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Creates a stream from php input stream |
206
|
|
|
* |
207
|
|
|
* @return StreamInterface |
208
|
|
|
*/ |
209
|
|
|
private function getPhpInputStream() |
210
|
|
|
{ |
211
|
|
|
$stream = new TextStream(file_get_contents('php://input')); |
212
|
|
|
return $stream; |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Check if provided files array is valid |
217
|
|
|
* |
218
|
|
|
* @param array $files |
219
|
|
|
* |
220
|
|
|
* @return bool |
221
|
|
|
*/ |
222
|
|
|
private function checkUploadedFiles(array $files) |
223
|
|
|
{ |
224
|
|
|
$valid = true; |
225
|
|
|
|
226
|
|
|
foreach ($files as $file) { |
227
|
|
|
if (is_array($file)) { |
228
|
|
|
$valid = $this->checkUploadedFiles($files); |
229
|
|
|
break; |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
if (! $file instanceof UploadedFile) { |
233
|
|
|
$valid = false; |
234
|
|
|
break; |
235
|
|
|
} |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
return $valid; |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Loads the headers form request |
243
|
|
|
*/ |
244
|
|
|
private function loadHeaders() |
245
|
|
|
{ |
246
|
|
|
foreach ($_SERVER as $key => $value) { |
247
|
|
|
$subset = substr($key, 0, 5); |
248
|
|
|
if ($subset <> 'HTTP_' && $subset <> 'CONTE') { |
249
|
|
|
continue; |
250
|
|
|
} |
251
|
|
|
$header = str_replace( |
252
|
|
|
' ', |
253
|
|
|
'-', |
254
|
|
|
ucwords( |
255
|
|
|
str_replace(['http_', '_'], ['', ' '], strtolower($key)) |
256
|
|
|
) |
257
|
|
|
); |
258
|
|
|
$this->headers[$this->headerKey($header)] = [$value]; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/** |
263
|
|
|
* Retrieve any parameters provided in the request body. |
264
|
|
|
* |
265
|
|
|
* @return null|array|object The deserialized body parameters, if any. |
266
|
|
|
* These will typically be an array or object. |
267
|
|
|
*/ |
268
|
|
|
public function getParsedBody() |
269
|
|
|
{ |
270
|
|
|
if (! $this->parsedBody) { |
271
|
|
|
$parser = new BodyParser($this->getHeaderLine('Content-Type')); |
272
|
|
|
$this->parsedBody = $parser->parse($this->getBody()); |
273
|
|
|
} |
274
|
|
|
return $this->parsedBody; |
275
|
|
|
} |
276
|
|
|
|
277
|
|
|
/** |
278
|
|
|
* Return an instance with the specified body parameters. |
279
|
|
|
* |
280
|
|
|
* @param null|array|object $data The deserialized body data. This will |
281
|
|
|
* typically be in an array or object. |
282
|
|
|
* |
283
|
|
|
* @return Request |
284
|
|
|
* @throws InvalidArgumentException if an unsupported argument type is |
285
|
|
|
* provided. |
286
|
|
|
*/ |
287
|
|
|
public function withParsedBody($data) |
288
|
|
|
{ |
289
|
|
|
if (! is_null($data) && |
290
|
|
|
! is_array($data) && |
291
|
|
|
! is_object($data) |
292
|
|
|
) { |
293
|
|
|
throw new InvalidArgumentException( |
294
|
|
|
"Only NULL, array or Object types could be used to ". |
295
|
|
|
"create a new server request message with parsed body." |
296
|
|
|
); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
$request = clone $this; |
300
|
|
|
$request->parsedBody = $data; |
301
|
|
|
return $request; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
/** |
305
|
|
|
* Retrieve attributes derived from the request. |
306
|
|
|
* |
307
|
|
|
* The request "attributes" may be used to allow injection of any |
308
|
|
|
* parameters derived from the request: e.g., the results of path |
309
|
|
|
* match operations; the results of decrypting cookies; the results of |
310
|
|
|
* deserializing non-form-encoded message bodies; etc. Attributes |
311
|
|
|
* will be application and request specific, and CAN be mutable. |
312
|
|
|
* |
313
|
|
|
* @return mixed[] Attributes derived from the request. |
314
|
|
|
*/ |
315
|
|
|
public function getAttributes() |
316
|
|
|
{ |
317
|
|
|
return $this->attributes; |
318
|
|
|
} |
319
|
|
|
|
320
|
|
|
/** |
321
|
|
|
* Return an instance with the specified derived request attribute. |
322
|
|
|
* |
323
|
|
|
* This method allows setting a single derived request attribute as |
324
|
|
|
* described in getAttributes(). |
325
|
|
|
* |
326
|
|
|
* @see getAttributes() |
327
|
|
|
* @param string $name The attribute name. |
328
|
|
|
* @param mixed $value The value of the attribute. |
329
|
|
|
* |
330
|
|
|
* @return Request |
331
|
|
|
*/ |
332
|
|
|
public function withAttribute($name, $value) |
333
|
|
|
{ |
334
|
|
|
$request = clone $this; |
335
|
|
|
$request->attributes[$name] = $value; |
336
|
|
|
return $request; |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Retrieve a single derived request attribute. |
341
|
|
|
* |
342
|
|
|
* Retrieves a single derived request attribute as described in |
343
|
|
|
* getAttributes(). If the attribute has not been previously set, returns |
344
|
|
|
* the default value as provided. |
345
|
|
|
* |
346
|
|
|
* @see getAttributes() |
347
|
|
|
* @param string $name The attribute name. |
348
|
|
|
* @param mixed $default Default value to return if the attribute does not exist. |
349
|
|
|
* @return mixed |
350
|
|
|
*/ |
351
|
|
|
public function getAttribute($name, $default = null) |
352
|
|
|
{ |
353
|
|
|
if (array_key_exists($name, $this->attributes)) { |
354
|
|
|
$default = $this->attributes[$name]; |
355
|
|
|
} |
356
|
|
|
return $default; |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
/** |
360
|
|
|
* Return an instance that removes the specified derived request attribute. |
361
|
|
|
* |
362
|
|
|
* This method allows removing a single derived request attribute as |
363
|
|
|
* described in getAttributes(). |
364
|
|
|
* |
365
|
|
|
* @see getAttributes() |
366
|
|
|
* @param string $name The attribute name. |
367
|
|
|
* |
368
|
|
|
* @return Request |
369
|
|
|
*/ |
370
|
|
|
public function withoutAttribute($name) |
371
|
|
|
{ |
372
|
|
|
$request = clone $this; |
373
|
|
|
if (array_key_exists($name, $request->attributes)) { |
374
|
|
|
unset($request->attributes[$name]); |
375
|
|
|
} |
376
|
|
|
return $request; |
377
|
|
|
} |
378
|
|
|
} |
379
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.