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