3 * @see https://github.com/zendframework/zend-diactoros for the canonical source repository
4 * @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
5 * @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
8 namespace Zend\Diactoros;
10 use function array_change_key_case;
11 use function array_key_exists;
14 use function is_array;
16 use function preg_match;
17 use function preg_replace;
20 use function strtolower;
24 * Marshal a Uri instance based on the values presnt in the $_SERVER array and headers.
26 * @param array $server SAPI parameters
27 * @param array $headers HTTP request headers
30 function marshalUriFromSapi(array $server, array $headers)
33 * Retrieve a header value from an array of headers using a case-insensitive lookup.
36 * @param array $headers Key/value header pairs
37 * @param mixed $default Default value to return if header not found
40 $getHeaderFromArray = function ($name, array $headers, $default = null) {
41 $header = strtolower($name);
42 $headers = array_change_key_case($headers, CASE_LOWER);
43 if (array_key_exists($header, $headers)) {
44 $value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
52 * Marshal the host and port from HTTP headers and/or the PHP environment.
54 * @param array $headers
55 * @param array $server
56 * @return array Array of two items, host and port, in that order (can be
57 * passed to a list() operation).
59 $marshalHostAndPort = function (array $headers, array $server) use ($getHeaderFromArray) {
61 * @param string|array $host
62 * @return array Array of two items, host and port, in that order (can be
63 * passed to a list() operation).
65 $marshalHostAndPortFromHeader = function ($host) {
66 if (is_array($host)) {
67 $host = implode(', ', $host);
72 // works for regname, IPv4 & IPv6
73 if (preg_match('|\:(\d+)$|', $host, $matches)) {
74 $host = substr($host, 0, -1 * (strlen($matches[1]) + 1));
75 $port = (int) $matches[1];
78 return [$host, $port];
82 * @param array $server
84 * @param null|int $port
85 * @return array Array of two items, host and port, in that order (can be
86 * passed to a list() operation).
88 $marshalIpv6HostAndPort = function (array $server, $host, $port) {
89 $host = '[' . $server['SERVER_ADDR'] . ']';
91 if ($port . ']' === substr($host, strrpos($host, ':') + 1)) {
92 // The last digit of the IPv6-Address has been taken as port
93 // Unset the port so the default port can be used
96 return [$host, $port];
99 static $defaults = ['', null];
101 if ($getHeaderFromArray('host', $headers, false)) {
102 return $marshalHostAndPortFromHeader($getHeaderFromArray('host', $headers));
105 if (! isset($server['SERVER_NAME'])) {
109 $host = $server['SERVER_NAME'];
110 $port = isset($server['SERVER_PORT']) ? (int) $server['SERVER_PORT'] : null;
112 if (! isset($server['SERVER_ADDR'])
113 || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $host)
115 return [$host, $port];
118 // Misinterpreted IPv6-Address
119 // Reported for Safari on Windows
120 return $marshalIpv6HostAndPort($server, $host, $port);
124 * Detect the path for the request
126 * Looks at a variety of criteria in order to attempt to autodetect the base
127 * request path, including:
129 * - IIS7 UrlRewrite environment
133 * From ZF2's Zend\Http\PhpEnvironment\Request class
134 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
135 * @license http://framework.zend.com/license/new-bsd New BSD License
137 * @param array $server SAPI environment array (typically `$_SERVER`)
138 * @return string Discovered path
140 $marshalRequestPath = function (array $server) {
141 // IIS7 with URL Rewrite: make sure we get the unencoded url
142 // (double slash problem).
143 $iisUrlRewritten = array_key_exists('IIS_WasUrlRewritten', $server) ? $server['IIS_WasUrlRewritten'] : null;
144 $unencodedUrl = array_key_exists('UNENCODED_URL', $server) ? $server['UNENCODED_URL'] : '';
145 if ('1' === $iisUrlRewritten && ! empty($unencodedUrl)) {
146 return $unencodedUrl;
149 $requestUri = array_key_exists('REQUEST_URI', $server) ? $server['REQUEST_URI'] : null;
151 if ($requestUri !== null) {
152 return preg_replace('#^[^/:]+://[^/]+#', '', $requestUri);
155 $origPathInfo = array_key_exists('ORIG_PATH_INFO', $server) ? $server['ORIG_PATH_INFO'] : null;
156 if (empty($origPathInfo)) {
160 return $origPathInfo;
167 if (array_key_exists('HTTPS', $server)) {
168 $https = $server['HTTPS'];
169 } elseif (array_key_exists('https', $server)) {
170 $https = $server['https'];
174 if (($https && 'off' !== strtolower($https))
175 || strtolower($getHeaderFromArray('x-forwarded-proto', $headers, false)) === 'https'
179 $uri = $uri->withScheme($scheme);
182 list($host, $port) = $marshalHostAndPort($headers, $server);
183 if (! empty($host)) {
184 $uri = $uri->withHost($host);
185 if (! empty($port)) {
186 $uri = $uri->withPort($port);
191 $path = $marshalRequestPath($server);
193 // Strip query string
194 $path = explode('?', $path, 2)[0];
198 if (isset($server['QUERY_STRING'])) {
199 $query = ltrim($server['QUERY_STRING'], '?');
204 if (strpos($path, '#') !== false) {
205 list($path, $fragment) = explode('#', $path, 2);
210 ->withFragment($fragment)