Chris@0
|
1 <?php
|
Chris@0
|
2 /**
|
Chris@12
|
3 * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
|
Chris@12
|
4 * @copyright Copyright (c) 2015-2017 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
5 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
|
Chris@0
|
6 */
|
Chris@0
|
7
|
Chris@0
|
8 namespace Zend\Diactoros;
|
Chris@0
|
9
|
Chris@0
|
10 use InvalidArgumentException;
|
Chris@0
|
11 use Psr\Http\Message\UploadedFileInterface;
|
Chris@0
|
12 use stdClass;
|
Chris@0
|
13 use UnexpectedValueException;
|
Chris@0
|
14
|
Chris@0
|
15 /**
|
Chris@0
|
16 * Class for marshaling a request object from the current PHP environment.
|
Chris@0
|
17 *
|
Chris@0
|
18 * Logic largely refactored from the ZF2 Zend\Http\PhpEnvironment\Request class.
|
Chris@0
|
19 *
|
Chris@0
|
20 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
21 * @license http://framework.zend.com/license/new-bsd New BSD License
|
Chris@0
|
22 */
|
Chris@0
|
23 abstract class ServerRequestFactory
|
Chris@0
|
24 {
|
Chris@0
|
25 /**
|
Chris@0
|
26 * Function to use to get apache request headers; present only to simplify mocking.
|
Chris@0
|
27 *
|
Chris@0
|
28 * @var callable
|
Chris@0
|
29 */
|
Chris@0
|
30 private static $apacheRequestHeaders = 'apache_request_headers';
|
Chris@0
|
31
|
Chris@0
|
32 /**
|
Chris@0
|
33 * Create a request from the supplied superglobal values.
|
Chris@0
|
34 *
|
Chris@0
|
35 * If any argument is not supplied, the corresponding superglobal value will
|
Chris@0
|
36 * be used.
|
Chris@0
|
37 *
|
Chris@0
|
38 * The ServerRequest created is then passed to the fromServer() method in
|
Chris@0
|
39 * order to marshal the request URI and headers.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @see fromServer()
|
Chris@0
|
42 * @param array $server $_SERVER superglobal
|
Chris@0
|
43 * @param array $query $_GET superglobal
|
Chris@0
|
44 * @param array $body $_POST superglobal
|
Chris@0
|
45 * @param array $cookies $_COOKIE superglobal
|
Chris@0
|
46 * @param array $files $_FILES superglobal
|
Chris@0
|
47 * @return ServerRequest
|
Chris@0
|
48 * @throws InvalidArgumentException for invalid file values
|
Chris@0
|
49 */
|
Chris@0
|
50 public static function fromGlobals(
|
Chris@0
|
51 array $server = null,
|
Chris@0
|
52 array $query = null,
|
Chris@0
|
53 array $body = null,
|
Chris@0
|
54 array $cookies = null,
|
Chris@0
|
55 array $files = null
|
Chris@0
|
56 ) {
|
Chris@0
|
57 $server = static::normalizeServer($server ?: $_SERVER);
|
Chris@0
|
58 $files = static::normalizeFiles($files ?: $_FILES);
|
Chris@0
|
59 $headers = static::marshalHeaders($server);
|
Chris@0
|
60
|
Chris@0
|
61 if (null === $cookies && array_key_exists('cookie', $headers)) {
|
Chris@0
|
62 $cookies = self::parseCookieHeader($headers['cookie']);
|
Chris@0
|
63 }
|
Chris@0
|
64
|
Chris@0
|
65 return new ServerRequest(
|
Chris@0
|
66 $server,
|
Chris@0
|
67 $files,
|
Chris@0
|
68 static::marshalUriFromServer($server, $headers),
|
Chris@0
|
69 static::get('REQUEST_METHOD', $server, 'GET'),
|
Chris@0
|
70 'php://input',
|
Chris@0
|
71 $headers,
|
Chris@0
|
72 $cookies ?: $_COOKIE,
|
Chris@0
|
73 $query ?: $_GET,
|
Chris@0
|
74 $body ?: $_POST,
|
Chris@0
|
75 static::marshalProtocolVersion($server)
|
Chris@0
|
76 );
|
Chris@0
|
77 }
|
Chris@0
|
78
|
Chris@0
|
79 /**
|
Chris@0
|
80 * Access a value in an array, returning a default value if not found
|
Chris@0
|
81 *
|
Chris@0
|
82 * Will also do a case-insensitive search if a case sensitive search fails.
|
Chris@0
|
83 *
|
Chris@0
|
84 * @param string $key
|
Chris@0
|
85 * @param array $values
|
Chris@0
|
86 * @param mixed $default
|
Chris@0
|
87 * @return mixed
|
Chris@0
|
88 */
|
Chris@0
|
89 public static function get($key, array $values, $default = null)
|
Chris@0
|
90 {
|
Chris@0
|
91 if (array_key_exists($key, $values)) {
|
Chris@0
|
92 return $values[$key];
|
Chris@0
|
93 }
|
Chris@0
|
94
|
Chris@0
|
95 return $default;
|
Chris@0
|
96 }
|
Chris@0
|
97
|
Chris@0
|
98 /**
|
Chris@0
|
99 * Search for a header value.
|
Chris@0
|
100 *
|
Chris@0
|
101 * Does a case-insensitive search for a matching header.
|
Chris@0
|
102 *
|
Chris@0
|
103 * If found, it is returned as a string, using comma concatenation.
|
Chris@0
|
104 *
|
Chris@0
|
105 * If not, the $default is returned.
|
Chris@0
|
106 *
|
Chris@0
|
107 * @param string $header
|
Chris@0
|
108 * @param array $headers
|
Chris@0
|
109 * @param mixed $default
|
Chris@0
|
110 * @return string
|
Chris@0
|
111 */
|
Chris@0
|
112 public static function getHeader($header, array $headers, $default = null)
|
Chris@0
|
113 {
|
Chris@0
|
114 $header = strtolower($header);
|
Chris@0
|
115 $headers = array_change_key_case($headers, CASE_LOWER);
|
Chris@0
|
116 if (array_key_exists($header, $headers)) {
|
Chris@0
|
117 $value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
|
Chris@0
|
118 return $value;
|
Chris@0
|
119 }
|
Chris@0
|
120
|
Chris@0
|
121 return $default;
|
Chris@0
|
122 }
|
Chris@0
|
123
|
Chris@0
|
124 /**
|
Chris@0
|
125 * Marshal the $_SERVER array
|
Chris@0
|
126 *
|
Chris@0
|
127 * Pre-processes and returns the $_SERVER superglobal.
|
Chris@0
|
128 *
|
Chris@0
|
129 * @param array $server
|
Chris@0
|
130 * @return array
|
Chris@0
|
131 */
|
Chris@0
|
132 public static function normalizeServer(array $server)
|
Chris@0
|
133 {
|
Chris@0
|
134 // This seems to be the only way to get the Authorization header on Apache
|
Chris@0
|
135 $apacheRequestHeaders = self::$apacheRequestHeaders;
|
Chris@0
|
136 if (isset($server['HTTP_AUTHORIZATION'])
|
Chris@0
|
137 || ! is_callable($apacheRequestHeaders)
|
Chris@0
|
138 ) {
|
Chris@0
|
139 return $server;
|
Chris@0
|
140 }
|
Chris@0
|
141
|
Chris@0
|
142 $apacheRequestHeaders = $apacheRequestHeaders();
|
Chris@0
|
143 if (isset($apacheRequestHeaders['Authorization'])) {
|
Chris@0
|
144 $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['Authorization'];
|
Chris@0
|
145 return $server;
|
Chris@0
|
146 }
|
Chris@0
|
147
|
Chris@0
|
148 if (isset($apacheRequestHeaders['authorization'])) {
|
Chris@0
|
149 $server['HTTP_AUTHORIZATION'] = $apacheRequestHeaders['authorization'];
|
Chris@0
|
150 return $server;
|
Chris@0
|
151 }
|
Chris@0
|
152
|
Chris@0
|
153 return $server;
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 /**
|
Chris@0
|
157 * Normalize uploaded files
|
Chris@0
|
158 *
|
Chris@0
|
159 * Transforms each value into an UploadedFileInterface instance, and ensures
|
Chris@0
|
160 * that nested arrays are normalized.
|
Chris@0
|
161 *
|
Chris@0
|
162 * @param array $files
|
Chris@0
|
163 * @return array
|
Chris@0
|
164 * @throws InvalidArgumentException for unrecognized values
|
Chris@0
|
165 */
|
Chris@0
|
166 public static function normalizeFiles(array $files)
|
Chris@0
|
167 {
|
Chris@0
|
168 $normalized = [];
|
Chris@0
|
169 foreach ($files as $key => $value) {
|
Chris@0
|
170 if ($value instanceof UploadedFileInterface) {
|
Chris@0
|
171 $normalized[$key] = $value;
|
Chris@0
|
172 continue;
|
Chris@0
|
173 }
|
Chris@0
|
174
|
Chris@0
|
175 if (is_array($value) && isset($value['tmp_name'])) {
|
Chris@0
|
176 $normalized[$key] = self::createUploadedFileFromSpec($value);
|
Chris@0
|
177 continue;
|
Chris@0
|
178 }
|
Chris@0
|
179
|
Chris@0
|
180 if (is_array($value)) {
|
Chris@0
|
181 $normalized[$key] = self::normalizeFiles($value);
|
Chris@0
|
182 continue;
|
Chris@0
|
183 }
|
Chris@0
|
184
|
Chris@0
|
185 throw new InvalidArgumentException('Invalid value in files specification');
|
Chris@0
|
186 }
|
Chris@0
|
187 return $normalized;
|
Chris@0
|
188 }
|
Chris@0
|
189
|
Chris@0
|
190 /**
|
Chris@0
|
191 * Marshal headers from $_SERVER
|
Chris@0
|
192 *
|
Chris@0
|
193 * @param array $server
|
Chris@0
|
194 * @return array
|
Chris@0
|
195 */
|
Chris@0
|
196 public static function marshalHeaders(array $server)
|
Chris@0
|
197 {
|
Chris@0
|
198 $headers = [];
|
Chris@0
|
199 foreach ($server as $key => $value) {
|
Chris@0
|
200 // Apache prefixes environment variables with REDIRECT_
|
Chris@0
|
201 // if they are added by rewrite rules
|
Chris@0
|
202 if (strpos($key, 'REDIRECT_') === 0) {
|
Chris@0
|
203 $key = substr($key, 9);
|
Chris@0
|
204
|
Chris@0
|
205 // We will not overwrite existing variables with the
|
Chris@0
|
206 // prefixed versions, though
|
Chris@0
|
207 if (array_key_exists($key, $server)) {
|
Chris@0
|
208 continue;
|
Chris@0
|
209 }
|
Chris@0
|
210 }
|
Chris@0
|
211
|
Chris@0
|
212 if ($value && strpos($key, 'HTTP_') === 0) {
|
Chris@0
|
213 $name = strtr(strtolower(substr($key, 5)), '_', '-');
|
Chris@0
|
214 $headers[$name] = $value;
|
Chris@0
|
215 continue;
|
Chris@0
|
216 }
|
Chris@0
|
217
|
Chris@0
|
218 if ($value && strpos($key, 'CONTENT_') === 0) {
|
Chris@0
|
219 $name = 'content-' . strtolower(substr($key, 8));
|
Chris@0
|
220 $headers[$name] = $value;
|
Chris@0
|
221 continue;
|
Chris@0
|
222 }
|
Chris@0
|
223 }
|
Chris@0
|
224
|
Chris@0
|
225 return $headers;
|
Chris@0
|
226 }
|
Chris@0
|
227
|
Chris@0
|
228 /**
|
Chris@0
|
229 * Marshal the URI from the $_SERVER array and headers
|
Chris@0
|
230 *
|
Chris@0
|
231 * @param array $server
|
Chris@0
|
232 * @param array $headers
|
Chris@0
|
233 * @return Uri
|
Chris@0
|
234 */
|
Chris@0
|
235 public static function marshalUriFromServer(array $server, array $headers)
|
Chris@0
|
236 {
|
Chris@0
|
237 $uri = new Uri('');
|
Chris@0
|
238
|
Chris@0
|
239 // URI scheme
|
Chris@0
|
240 $scheme = 'http';
|
Chris@0
|
241 $https = self::get('HTTPS', $server);
|
Chris@0
|
242 if (($https && 'off' !== $https)
|
Chris@0
|
243 || self::getHeader('x-forwarded-proto', $headers, false) === 'https'
|
Chris@0
|
244 ) {
|
Chris@0
|
245 $scheme = 'https';
|
Chris@0
|
246 }
|
Chris@0
|
247 if (! empty($scheme)) {
|
Chris@0
|
248 $uri = $uri->withScheme($scheme);
|
Chris@0
|
249 }
|
Chris@0
|
250
|
Chris@0
|
251 // Set the host
|
Chris@0
|
252 $accumulator = (object) ['host' => '', 'port' => null];
|
Chris@0
|
253 self::marshalHostAndPortFromHeaders($accumulator, $server, $headers);
|
Chris@0
|
254 $host = $accumulator->host;
|
Chris@0
|
255 $port = $accumulator->port;
|
Chris@0
|
256 if (! empty($host)) {
|
Chris@0
|
257 $uri = $uri->withHost($host);
|
Chris@0
|
258 if (! empty($port)) {
|
Chris@0
|
259 $uri = $uri->withPort($port);
|
Chris@0
|
260 }
|
Chris@0
|
261 }
|
Chris@0
|
262
|
Chris@0
|
263 // URI path
|
Chris@0
|
264 $path = self::marshalRequestUri($server);
|
Chris@0
|
265 $path = self::stripQueryString($path);
|
Chris@0
|
266
|
Chris@0
|
267 // URI query
|
Chris@0
|
268 $query = '';
|
Chris@0
|
269 if (isset($server['QUERY_STRING'])) {
|
Chris@0
|
270 $query = ltrim($server['QUERY_STRING'], '?');
|
Chris@0
|
271 }
|
Chris@0
|
272
|
Chris@0
|
273 // URI fragment
|
Chris@0
|
274 $fragment = '';
|
Chris@0
|
275 if (strpos($path, '#') !== false) {
|
Chris@0
|
276 list($path, $fragment) = explode('#', $path, 2);
|
Chris@0
|
277 }
|
Chris@0
|
278
|
Chris@0
|
279 return $uri
|
Chris@0
|
280 ->withPath($path)
|
Chris@0
|
281 ->withFragment($fragment)
|
Chris@0
|
282 ->withQuery($query);
|
Chris@0
|
283 }
|
Chris@0
|
284
|
Chris@0
|
285 /**
|
Chris@0
|
286 * Marshal the host and port from HTTP headers and/or the PHP environment
|
Chris@0
|
287 *
|
Chris@0
|
288 * @param stdClass $accumulator
|
Chris@0
|
289 * @param array $server
|
Chris@0
|
290 * @param array $headers
|
Chris@0
|
291 */
|
Chris@0
|
292 public static function marshalHostAndPortFromHeaders(stdClass $accumulator, array $server, array $headers)
|
Chris@0
|
293 {
|
Chris@0
|
294 if (self::getHeader('host', $headers, false)) {
|
Chris@0
|
295 self::marshalHostAndPortFromHeader($accumulator, self::getHeader('host', $headers));
|
Chris@0
|
296 return;
|
Chris@0
|
297 }
|
Chris@0
|
298
|
Chris@0
|
299 if (! isset($server['SERVER_NAME'])) {
|
Chris@0
|
300 return;
|
Chris@0
|
301 }
|
Chris@0
|
302
|
Chris@0
|
303 $accumulator->host = $server['SERVER_NAME'];
|
Chris@0
|
304 if (isset($server['SERVER_PORT'])) {
|
Chris@0
|
305 $accumulator->port = (int) $server['SERVER_PORT'];
|
Chris@0
|
306 }
|
Chris@0
|
307
|
Chris@0
|
308 if (! isset($server['SERVER_ADDR']) || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $accumulator->host)) {
|
Chris@0
|
309 return;
|
Chris@0
|
310 }
|
Chris@0
|
311
|
Chris@0
|
312 // Misinterpreted IPv6-Address
|
Chris@0
|
313 // Reported for Safari on Windows
|
Chris@0
|
314 self::marshalIpv6HostAndPort($accumulator, $server);
|
Chris@0
|
315 }
|
Chris@0
|
316
|
Chris@0
|
317 /**
|
Chris@0
|
318 * Detect the base URI for the request
|
Chris@0
|
319 *
|
Chris@0
|
320 * Looks at a variety of criteria in order to attempt to autodetect a base
|
Chris@0
|
321 * URI, including rewrite URIs, proxy URIs, etc.
|
Chris@0
|
322 *
|
Chris@0
|
323 * From ZF2's Zend\Http\PhpEnvironment\Request class
|
Chris@0
|
324 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
|
Chris@0
|
325 * @license http://framework.zend.com/license/new-bsd New BSD License
|
Chris@0
|
326 *
|
Chris@0
|
327 * @param array $server
|
Chris@0
|
328 * @return string
|
Chris@0
|
329 */
|
Chris@0
|
330 public static function marshalRequestUri(array $server)
|
Chris@0
|
331 {
|
Chris@0
|
332 // IIS7 with URL Rewrite: make sure we get the unencoded url
|
Chris@0
|
333 // (double slash problem).
|
Chris@0
|
334 $iisUrlRewritten = self::get('IIS_WasUrlRewritten', $server);
|
Chris@0
|
335 $unencodedUrl = self::get('UNENCODED_URL', $server, '');
|
Chris@0
|
336 if ('1' == $iisUrlRewritten && ! empty($unencodedUrl)) {
|
Chris@0
|
337 return $unencodedUrl;
|
Chris@0
|
338 }
|
Chris@0
|
339
|
Chris@0
|
340 $requestUri = self::get('REQUEST_URI', $server);
|
Chris@0
|
341
|
Chris@0
|
342 // Check this first so IIS will catch.
|
Chris@0
|
343 $httpXRewriteUrl = self::get('HTTP_X_REWRITE_URL', $server);
|
Chris@0
|
344 if ($httpXRewriteUrl !== null) {
|
Chris@0
|
345 $requestUri = $httpXRewriteUrl;
|
Chris@0
|
346 }
|
Chris@0
|
347
|
Chris@0
|
348 // Check for IIS 7.0 or later with ISAPI_Rewrite
|
Chris@0
|
349 $httpXOriginalUrl = self::get('HTTP_X_ORIGINAL_URL', $server);
|
Chris@0
|
350 if ($httpXOriginalUrl !== null) {
|
Chris@0
|
351 $requestUri = $httpXOriginalUrl;
|
Chris@0
|
352 }
|
Chris@0
|
353
|
Chris@0
|
354 if ($requestUri !== null) {
|
Chris@0
|
355 return preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
|
Chris@0
|
356 }
|
Chris@0
|
357
|
Chris@0
|
358 $origPathInfo = self::get('ORIG_PATH_INFO', $server);
|
Chris@0
|
359 if (empty($origPathInfo)) {
|
Chris@0
|
360 return '/';
|
Chris@0
|
361 }
|
Chris@0
|
362
|
Chris@0
|
363 return $origPathInfo;
|
Chris@0
|
364 }
|
Chris@0
|
365
|
Chris@0
|
366 /**
|
Chris@0
|
367 * Strip the query string from a path
|
Chris@0
|
368 *
|
Chris@0
|
369 * @param mixed $path
|
Chris@0
|
370 * @return string
|
Chris@0
|
371 */
|
Chris@0
|
372 public static function stripQueryString($path)
|
Chris@0
|
373 {
|
Chris@0
|
374 if (($qpos = strpos($path, '?')) !== false) {
|
Chris@0
|
375 return substr($path, 0, $qpos);
|
Chris@0
|
376 }
|
Chris@0
|
377 return $path;
|
Chris@0
|
378 }
|
Chris@0
|
379
|
Chris@0
|
380 /**
|
Chris@0
|
381 * Marshal the host and port from the request header
|
Chris@0
|
382 *
|
Chris@0
|
383 * @param stdClass $accumulator
|
Chris@0
|
384 * @param string|array $host
|
Chris@0
|
385 * @return void
|
Chris@0
|
386 */
|
Chris@0
|
387 private static function marshalHostAndPortFromHeader(stdClass $accumulator, $host)
|
Chris@0
|
388 {
|
Chris@0
|
389 if (is_array($host)) {
|
Chris@0
|
390 $host = implode(', ', $host);
|
Chris@0
|
391 }
|
Chris@0
|
392
|
Chris@0
|
393 $accumulator->host = $host;
|
Chris@0
|
394 $accumulator->port = null;
|
Chris@0
|
395
|
Chris@0
|
396 // works for regname, IPv4 & IPv6
|
Chris@0
|
397 if (preg_match('|\:(\d+)$|', $accumulator->host, $matches)) {
|
Chris@0
|
398 $accumulator->host = substr($accumulator->host, 0, -1 * (strlen($matches[1]) + 1));
|
Chris@0
|
399 $accumulator->port = (int) $matches[1];
|
Chris@0
|
400 }
|
Chris@0
|
401 }
|
Chris@0
|
402
|
Chris@0
|
403 /**
|
Chris@0
|
404 * Marshal host/port from misinterpreted IPv6 address
|
Chris@0
|
405 *
|
Chris@0
|
406 * @param stdClass $accumulator
|
Chris@0
|
407 * @param array $server
|
Chris@0
|
408 */
|
Chris@0
|
409 private static function marshalIpv6HostAndPort(stdClass $accumulator, array $server)
|
Chris@0
|
410 {
|
Chris@0
|
411 $accumulator->host = '[' . $server['SERVER_ADDR'] . ']';
|
Chris@0
|
412 $accumulator->port = $accumulator->port ?: 80;
|
Chris@0
|
413 if ($accumulator->port . ']' === substr($accumulator->host, strrpos($accumulator->host, ':') + 1)) {
|
Chris@0
|
414 // The last digit of the IPv6-Address has been taken as port
|
Chris@0
|
415 // Unset the port so the default port can be used
|
Chris@0
|
416 $accumulator->port = null;
|
Chris@0
|
417 }
|
Chris@0
|
418 }
|
Chris@0
|
419
|
Chris@0
|
420 /**
|
Chris@0
|
421 * Create and return an UploadedFile instance from a $_FILES specification.
|
Chris@0
|
422 *
|
Chris@0
|
423 * If the specification represents an array of values, this method will
|
Chris@0
|
424 * delegate to normalizeNestedFileSpec() and return that return value.
|
Chris@0
|
425 *
|
Chris@0
|
426 * @param array $value $_FILES struct
|
Chris@0
|
427 * @return array|UploadedFileInterface
|
Chris@0
|
428 */
|
Chris@0
|
429 private static function createUploadedFileFromSpec(array $value)
|
Chris@0
|
430 {
|
Chris@0
|
431 if (is_array($value['tmp_name'])) {
|
Chris@0
|
432 return self::normalizeNestedFileSpec($value);
|
Chris@0
|
433 }
|
Chris@0
|
434
|
Chris@0
|
435 return new UploadedFile(
|
Chris@0
|
436 $value['tmp_name'],
|
Chris@0
|
437 $value['size'],
|
Chris@0
|
438 $value['error'],
|
Chris@0
|
439 $value['name'],
|
Chris@0
|
440 $value['type']
|
Chris@0
|
441 );
|
Chris@0
|
442 }
|
Chris@0
|
443
|
Chris@0
|
444 /**
|
Chris@0
|
445 * Normalize an array of file specifications.
|
Chris@0
|
446 *
|
Chris@0
|
447 * Loops through all nested files and returns a normalized array of
|
Chris@0
|
448 * UploadedFileInterface instances.
|
Chris@0
|
449 *
|
Chris@0
|
450 * @param array $files
|
Chris@0
|
451 * @return UploadedFileInterface[]
|
Chris@0
|
452 */
|
Chris@0
|
453 private static function normalizeNestedFileSpec(array $files = [])
|
Chris@0
|
454 {
|
Chris@0
|
455 $normalizedFiles = [];
|
Chris@0
|
456 foreach (array_keys($files['tmp_name']) as $key) {
|
Chris@0
|
457 $spec = [
|
Chris@0
|
458 'tmp_name' => $files['tmp_name'][$key],
|
Chris@0
|
459 'size' => $files['size'][$key],
|
Chris@0
|
460 'error' => $files['error'][$key],
|
Chris@0
|
461 'name' => $files['name'][$key],
|
Chris@0
|
462 'type' => $files['type'][$key],
|
Chris@0
|
463 ];
|
Chris@0
|
464 $normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
|
Chris@0
|
465 }
|
Chris@0
|
466 return $normalizedFiles;
|
Chris@0
|
467 }
|
Chris@0
|
468
|
Chris@0
|
469 /**
|
Chris@0
|
470 * Return HTTP protocol version (X.Y)
|
Chris@0
|
471 *
|
Chris@0
|
472 * @param array $server
|
Chris@0
|
473 * @return string
|
Chris@0
|
474 */
|
Chris@0
|
475 private static function marshalProtocolVersion(array $server)
|
Chris@0
|
476 {
|
Chris@0
|
477 if (! isset($server['SERVER_PROTOCOL'])) {
|
Chris@0
|
478 return '1.1';
|
Chris@0
|
479 }
|
Chris@0
|
480
|
Chris@0
|
481 if (! preg_match('#^(HTTP/)?(?P<version>[1-9]\d*(?:\.\d)?)$#', $server['SERVER_PROTOCOL'], $matches)) {
|
Chris@0
|
482 throw new UnexpectedValueException(sprintf(
|
Chris@0
|
483 'Unrecognized protocol version (%s)',
|
Chris@0
|
484 $server['SERVER_PROTOCOL']
|
Chris@0
|
485 ));
|
Chris@0
|
486 }
|
Chris@0
|
487
|
Chris@0
|
488 return $matches['version'];
|
Chris@0
|
489 }
|
Chris@0
|
490
|
Chris@0
|
491 /**
|
Chris@0
|
492 * Parse a cookie header according to RFC 6265.
|
Chris@0
|
493 *
|
Chris@0
|
494 * PHP will replace special characters in cookie names, which results in other cookies not being available due to
|
Chris@0
|
495 * overwriting. Thus, the server request should take the cookies from the request header instead.
|
Chris@0
|
496 *
|
Chris@0
|
497 * @param $cookieHeader
|
Chris@0
|
498 * @return array
|
Chris@0
|
499 */
|
Chris@0
|
500 private static function parseCookieHeader($cookieHeader)
|
Chris@0
|
501 {
|
Chris@0
|
502 preg_match_all('(
|
Chris@0
|
503 (?:^\\n?[ \t]*|;[ ])
|
Chris@0
|
504 (?P<name>[!#$%&\'*+-.0-9A-Z^_`a-z|~]+)
|
Chris@0
|
505 =
|
Chris@0
|
506 (?P<DQUOTE>"?)
|
Chris@0
|
507 (?P<value>[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*)
|
Chris@0
|
508 (?P=DQUOTE)
|
Chris@0
|
509 (?=\\n?[ \t]*$|;[ ])
|
Chris@0
|
510 )x', $cookieHeader, $matches, PREG_SET_ORDER);
|
Chris@0
|
511
|
Chris@0
|
512 $cookies = [];
|
Chris@0
|
513
|
Chris@0
|
514 foreach ($matches as $match) {
|
Chris@0
|
515 $cookies[$match['name']] = urldecode($match['value']);
|
Chris@0
|
516 }
|
Chris@0
|
517
|
Chris@0
|
518 return $cookies;
|
Chris@0
|
519 }
|
Chris@0
|
520 }
|