krz/vox-populi
A Tumblr web client.
clone: git clone https://gitbay.org/krz/vox-populi.git
main: vendor/guzzlehttp/psr7/src/ServerRequest.php · raw
1<?php
2
3namespace GuzzleHttp\Psr7;
4
5use InvalidArgumentException;
6use Psr\Http\Message\ServerRequestInterface;
7use Psr\Http\Message\UriInterface;
8use Psr\Http\Message\StreamInterface;
9use Psr\Http\Message\UploadedFileInterface;
10
11/**
12 * Server-side HTTP request
13 *
14 * Extends the Request definition to add methods for accessing incoming data,
15 * specifically server parameters, cookies, matched path parameters, query
16 * string arguments, body parameters, and upload file information.
17 *
18 * "Attributes" are discovered via decomposing the request (and usually
19 * specifically the URI path), and typically will be injected by the application.
20 *
21 * Requests are considered immutable; all methods that might change state are
22 * implemented such that they retain the internal state of the current
23 * message and return a new instance that contains the changed state.
24 */
25class ServerRequest extends Request implements ServerRequestInterface
26{
27 /**
28 * @var array
29 */
30 private $attributes = [];
31
32 /**
33 * @var array
34 */
35 private $cookieParams = [];
36
37 /**
38 * @var null|array|object
39 */
40 private $parsedBody;
41
42 /**
43 * @var array
44 */
45 private $queryParams = [];
46
47 /**
48 * @var array
49 */
50 private $serverParams;
51
52 /**
53 * @var array
54 */
55 private $uploadedFiles = [];
56
57 /**
58 * @param string $method HTTP method
59 * @param string|UriInterface $uri URI
60 * @param array $headers Request headers
61 * @param string|null|resource|StreamInterface $body Request body
62 * @param string $version Protocol version
63 * @param array $serverParams Typically the $_SERVER superglobal
64 */
65 public function __construct(
66 $method,
67 $uri,
68 array $headers = [],
69 $body = null,
70 $version = '1.1',
71 array $serverParams = []
72 ) {
73 $this->serverParams = $serverParams;
74
75 parent::__construct($method, $uri, $headers, $body, $version);
76 }
77
78 /**
79 * Return an UploadedFile instance array.
80 *
81 * @param array $files A array which respect $_FILES structure
82 * @throws InvalidArgumentException for unrecognized values
83 * @return array
84 */
85 public static function normalizeFiles(array $files)
86 {
87 $normalized = [];
88
89 foreach ($files as $key => $value) {
90 if ($value instanceof UploadedFileInterface) {
91 $normalized[$key] = $value;
92 } elseif (is_array($value) && isset($value['tmp_name'])) {
93 $normalized[$key] = self::createUploadedFileFromSpec($value);
94 } elseif (is_array($value)) {
95 $normalized[$key] = self::normalizeFiles($value);
96 continue;
97 } else {
98 throw new InvalidArgumentException('Invalid value in files specification');
99 }
100 }
101
102 return $normalized;
103 }
104
105 /**
106 * Create and return an UploadedFile instance from a $_FILES specification.
107 *
108 * If the specification represents an array of values, this method will
109 * delegate to normalizeNestedFileSpec() and return that return value.
110 *
111 * @param array $value $_FILES struct
112 * @return array|UploadedFileInterface
113 */
114 private static function createUploadedFileFromSpec(array $value)
115 {
116 if (is_array($value['tmp_name'])) {
117 return self::normalizeNestedFileSpec($value);
118 }
119
120 return new UploadedFile(
121 $value['tmp_name'],
122 (int) $value['size'],
123 (int) $value['error'],
124 $value['name'],
125 $value['type']
126 );
127 }
128
129 /**
130 * Normalize an array of file specifications.
131 *
132 * Loops through all nested files and returns a normalized array of
133 * UploadedFileInterface instances.
134 *
135 * @param array $files
136 * @return UploadedFileInterface[]
137 */
138 private static function normalizeNestedFileSpec(array $files = [])
139 {
140 $normalizedFiles = [];
141
142 foreach (array_keys($files['tmp_name']) as $key) {
143 $spec = [
144 'tmp_name' => $files['tmp_name'][$key],
145 'size' => $files['size'][$key],
146 'error' => $files['error'][$key],
147 'name' => $files['name'][$key],
148 'type' => $files['type'][$key],
149 ];
150 $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
151 }
152
153 return $normalizedFiles;
154 }
155
156 /**
157 * Return a ServerRequest populated with superglobals:
158 * $_GET
159 * $_POST
160 * $_COOKIE
161 * $_FILES
162 * $_SERVER
163 *
164 * @return ServerRequestInterface
165 */
166 public static function fromGlobals()
167 {
168 $method = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET';
169 $headers = getallheaders();
170 $uri = self::getUriFromGlobals();
171 $body = new CachingStream(new LazyOpenStream('php://input', 'r+'));
172 $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? str_replace('HTTP/', '', $_SERVER['SERVER_PROTOCOL']) : '1.1';
173
174 $serverRequest = new ServerRequest($method, $uri, $headers, $body, $protocol, $_SERVER);
175
176 return $serverRequest
177 ->withCookieParams($_COOKIE)
178 ->withQueryParams($_GET)
179 ->withParsedBody($_POST)
180 ->withUploadedFiles(self::normalizeFiles($_FILES));
181 }
182
183 private static function extractHostAndPortFromAuthority($authority)
184 {
185 $uri = 'http://'.$authority;
186 $parts = parse_url($uri);
187 if (false === $parts) {
188 return [null, null];
189 }
190
191 $host = isset($parts['host']) ? $parts['host'] : null;
192 $port = isset($parts['port']) ? $parts['port'] : null;
193
194 return [$host, $port];
195 }
196
197 /**
198 * Get a Uri populated with values from $_SERVER.
199 *
200 * @return UriInterface
201 */
202 public static function getUriFromGlobals()
203 {
204 $uri = new Uri('');
205
206 $uri = $uri->withScheme(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' ? 'https' : 'http');
207
208 $hasPort = false;
209 if (isset($_SERVER['HTTP_HOST'])) {
210 list($host, $port) = self::extractHostAndPortFromAuthority($_SERVER['HTTP_HOST']);
211 if ($host !== null) {
212 $uri = $uri->withHost($host);
213 }
214
215 if ($port !== null) {
216 $hasPort = true;
217 $uri = $uri->withPort($port);
218 }
219 } elseif (isset($_SERVER['SERVER_NAME'])) {
220 $uri = $uri->withHost($_SERVER['SERVER_NAME']);
221 } elseif (isset($_SERVER['SERVER_ADDR'])) {
222 $uri = $uri->withHost($_SERVER['SERVER_ADDR']);
223 }
224
225 if (!$hasPort && isset($_SERVER['SERVER_PORT'])) {
226 $uri = $uri->withPort($_SERVER['SERVER_PORT']);
227 }
228
229 $hasQuery = false;
230 if (isset($_SERVER['REQUEST_URI'])) {
231 $requestUriParts = explode('?', $_SERVER['REQUEST_URI'], 2);
232 $uri = $uri->withPath($requestUriParts[0]);
233 if (isset($requestUriParts[1])) {
234 $hasQuery = true;
235 $uri = $uri->withQuery($requestUriParts[1]);
236 }
237 }
238
239 if (!$hasQuery && isset($_SERVER['QUERY_STRING'])) {
240 $uri = $uri->withQuery($_SERVER['QUERY_STRING']);
241 }
242
243 return $uri;
244 }
245
246
247 /**
248 * {@inheritdoc}
249 */
250 public function getServerParams()
251 {
252 return $this->serverParams;
253 }
254
255 /**
256 * {@inheritdoc}
257 */
258 public function getUploadedFiles()
259 {
260 return $this->uploadedFiles;
261 }
262
263 /**
264 * {@inheritdoc}
265 */
266 public function withUploadedFiles(array $uploadedFiles)
267 {
268 $new = clone $this;
269 $new->uploadedFiles = $uploadedFiles;
270
271 return $new;
272 }
273
274 /**
275 * {@inheritdoc}
276 */
277 public function getCookieParams()
278 {
279 return $this->cookieParams;
280 }
281
282 /**
283 * {@inheritdoc}
284 */
285 public function withCookieParams(array $cookies)
286 {
287 $new = clone $this;
288 $new->cookieParams = $cookies;
289
290 return $new;
291 }
292
293 /**
294 * {@inheritdoc}
295 */
296 public function getQueryParams()
297 {
298 return $this->queryParams;
299 }
300
301 /**
302 * {@inheritdoc}
303 */
304 public function withQueryParams(array $query)
305 {
306 $new = clone $this;
307 $new->queryParams = $query;
308
309 return $new;
310 }
311
312 /**
313 * {@inheritdoc}
314 */
315 public function getParsedBody()
316 {
317 return $this->parsedBody;
318 }
319
320 /**
321 * {@inheritdoc}
322 */
323 public function withParsedBody($data)
324 {
325 $new = clone $this;
326 $new->parsedBody = $data;
327
328 return $new;
329 }
330
331 /**
332 * {@inheritdoc}
333 */
334 public function getAttributes()
335 {
336 return $this->attributes;
337 }
338
339 /**
340 * {@inheritdoc}
341 */
342 public function getAttribute($attribute, $default = null)
343 {
344 if (false === array_key_exists($attribute, $this->attributes)) {
345 return $default;
346 }
347
348 return $this->attributes[$attribute];
349 }
350
351 /**
352 * {@inheritdoc}
353 */
354 public function withAttribute($attribute, $value)
355 {
356 $new = clone $this;
357 $new->attributes[$attribute] = $value;
358
359 return $new;
360 }
361
362 /**
363 * {@inheritdoc}
364 */
365 public function withoutAttribute($attribute)
366 {
367 if (false === array_key_exists($attribute, $this->attributes)) {
368 return $this;
369 }
370
371 $new = clone $this;
372 unset($new->attributes[$attribute]);
373
374 return $new;
375 }
376}