comparison vendor/zendframework/zend-diactoros/src/ServerRequestFactory.php @ 16:c2387f117808

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