Skip to content
This repository has been archived by the owner on Jan 29, 2020. It is now read-only.

Commit

Permalink
Merge 17b49fb into 26efe24
Browse files Browse the repository at this point in the history
  • Loading branch information
weierophinney committed Jun 26, 2018
2 parents 26efe24 + 17b49fb commit 51b6629
Show file tree
Hide file tree
Showing 11 changed files with 387 additions and 241 deletions.
9 changes: 9 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
"psr/http-message-implementation": "1.0"
},
"autoload": {
"files": [
"src/functions/create_uploaded_file.php",
"src/functions/get_header_from_array.php",
"src/functions/marshal_host_and_port.php",
"src/functions/marshal_protocol_version.php",
"src/functions/normalize_uploaded_files.php",
"src/functions/normalize_uploaded_file_specification.php",
"src/functions/parse_cookie_header.php"
],
"psr-4": {
"Zend\\Diactoros\\": "src/"
}
Expand Down
2 changes: 1 addition & 1 deletion composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

210 changes: 13 additions & 197 deletions src/ServerRequestFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ public static function fromGlobals(
array $files = null
) {
$server = static::normalizeServer($server ?: $_SERVER);
$files = static::normalizeFiles($files ?: $_FILES);
$files = normalizeUploadedFiles($files ?: $_FILES);
$headers = static::marshalHeaders($server);

if (null === $cookies && array_key_exists('cookie', $headers)) {
$cookies = self::parseCookieHeader($headers['cookie']);
$cookies = parseCookieHeader($headers['cookie']);
}

return new ServerRequest(
Expand All @@ -95,7 +95,7 @@ public static function fromGlobals(
$cookies ?: $_COOKIE,
$query ?: $_GET,
$body ?: $_POST,
static::marshalProtocolVersion($server)
marshalProtocolVersion($server)
);
}

Expand Down Expand Up @@ -127,21 +127,15 @@ public static function get($key, array $values, $default = null)
*
* If not, the $default is returned.
*
* @deprecated since 1.8.0; use Zend\Diactoros\getHeaderFromArray() instead.
* @param string $header
* @param array $headers
* @param mixed $default
* @return string
*/
public static function getHeader($header, array $headers, $default = null)
{
$header = strtolower($header);
$headers = array_change_key_case($headers, CASE_LOWER);
if (array_key_exists($header, $headers)) {
$value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
return $value;
}

return $default;
return getHeaderFromArray($header, $headers, $default);
}

/**
Expand Down Expand Up @@ -182,32 +176,14 @@ public static function normalizeServer(array $server)
* Transforms each value into an UploadedFileInterface instance, and ensures
* that nested arrays are normalized.
*
* @deprecated since 1.8.0; use \Zend\Diactoros\normalizeUploadedFiles instead.
* @param array $files
* @return array
* @throws InvalidArgumentException for unrecognized values
*/
public static function normalizeFiles(array $files)
{
$normalized = [];
foreach ($files as $key => $value) {
if ($value instanceof UploadedFileInterface) {
$normalized[$key] = $value;
continue;
}

if (is_array($value) && isset($value['tmp_name'])) {
$normalized[$key] = self::createUploadedFileFromSpec($value);
continue;
}

if (is_array($value)) {
$normalized[$key] = self::normalizeFiles($value);
continue;
}

throw new InvalidArgumentException('Invalid value in files specification');
}
return $normalized;
return normalizeUploadedFiles($files);
}

/**
Expand Down Expand Up @@ -263,17 +239,15 @@ public static function marshalUriFromServer(array $server, array $headers)
$scheme = 'http';
$https = self::get('HTTPS', $server);
if (($https && 'off' !== $https)
|| self::getHeader('x-forwarded-proto', $headers, false) === 'https'
|| getHeaderFromArray('x-forwarded-proto', $headers, false) === 'https'
) {
$scheme = 'https';
}
$uri = $uri->withScheme($scheme);

// Set the host
$accumulator = (object) ['host' => '', 'port' => null];
self::marshalHostAndPortFromHeaders($accumulator, $server, $headers);
$host = $accumulator->host;
$port = $accumulator->port;
list($host, $port) = marshalHostAndPort($headers, $server);
if (! empty($host)) {
$uri = $uri->withHost($host);
if (! empty($port)) {
Expand Down Expand Up @@ -306,33 +280,16 @@ public static function marshalUriFromServer(array $server, array $headers)
/**
* Marshal the host and port from HTTP headers and/or the PHP environment
*
* @deprecated since 1.8.0; use Zend\Diactoros\marshalHostAndPort instead.
* @param stdClass $accumulator
* @param array $server
* @param array $headers
*/
public static function marshalHostAndPortFromHeaders(stdClass $accumulator, array $server, array $headers)
{
if (self::getHeader('host', $headers, false)) {
self::marshalHostAndPortFromHeader($accumulator, self::getHeader('host', $headers));
return;
}

if (! isset($server['SERVER_NAME'])) {
return;
}

$accumulator->host = $server['SERVER_NAME'];
if (isset($server['SERVER_PORT'])) {
$accumulator->port = (int) $server['SERVER_PORT'];
}

if (! isset($server['SERVER_ADDR']) || ! preg_match('/^\[[0-9a-fA-F\:]+\]$/', $accumulator->host)) {
return;
}

// Misinterpreted IPv6-Address
// Reported for Safari on Windows
self::marshalIpv6HostAndPort($accumulator, $server);
list($host, $port) = marshalHostAndPort($headers, $server);
$accumulator->host = $host;
$accumulator->port = $port;
}

/**
Expand Down Expand Up @@ -397,145 +354,4 @@ public static function stripQueryString($path)
}
return $path;
}

/**
* Marshal the host and port from the request header
*
* @param stdClass $accumulator
* @param string|array $host
* @return void
*/
private static function marshalHostAndPortFromHeader(stdClass $accumulator, $host)
{
if (is_array($host)) {
$host = implode(', ', $host);
}

$accumulator->host = $host;
$accumulator->port = null;

// works for regname, IPv4 & IPv6
if (preg_match('|\:(\d+)$|', $accumulator->host, $matches)) {
$accumulator->host = substr($accumulator->host, 0, -1 * (strlen($matches[1]) + 1));
$accumulator->port = (int) $matches[1];
}
}

/**
* Marshal host/port from misinterpreted IPv6 address
*
* @param stdClass $accumulator
* @param array $server
*/
private static function marshalIpv6HostAndPort(stdClass $accumulator, array $server)
{
$accumulator->host = '[' . $server['SERVER_ADDR'] . ']';
$accumulator->port = $accumulator->port ?: 80;
if ($accumulator->port . ']' === substr($accumulator->host, strrpos($accumulator->host, ':') + 1)) {
// The last digit of the IPv6-Address has been taken as port
// Unset the port so the default port can be used
$accumulator->port = null;
}
}

/**
* Create and return an UploadedFile instance from a $_FILES specification.
*
* If the specification represents an array of values, this method will
* delegate to normalizeNestedFileSpec() and return that return value.
*
* @param array $value $_FILES struct
* @return array|UploadedFileInterface
*/
private static function createUploadedFileFromSpec(array $value)
{
if (is_array($value['tmp_name'])) {
return self::normalizeNestedFileSpec($value);
}

return new UploadedFile(
$value['tmp_name'],
$value['size'],
$value['error'],
$value['name'],
$value['type']
);
}

/**
* Normalize an array of file specifications.
*
* Loops through all nested files and returns a normalized array of
* UploadedFileInterface instances.
*
* @param array $files
* @return UploadedFileInterface[]
*/
private static function normalizeNestedFileSpec(array $files = [])
{
$normalizedFiles = [];
foreach (array_keys($files['tmp_name']) as $key) {
$spec = [
'tmp_name' => $files['tmp_name'][$key],
'size' => $files['size'][$key],
'error' => $files['error'][$key],
'name' => $files['name'][$key],
'type' => $files['type'][$key],
];
$normalizedFiles[$key] = self::createUploadedFileFromSpec($spec);
}
return $normalizedFiles;
}

/**
* Return HTTP protocol version (X.Y)
*
* @param array $server
* @return string
*/
private static function marshalProtocolVersion(array $server)
{
if (! isset($server['SERVER_PROTOCOL'])) {
return '1.1';
}

if (! preg_match('#^(HTTP/)?(?P<version>[1-9]\d*(?:\.\d)?)$#', $server['SERVER_PROTOCOL'], $matches)) {
throw new UnexpectedValueException(sprintf(
'Unrecognized protocol version (%s)',
$server['SERVER_PROTOCOL']
));
}

return $matches['version'];
}

/**
* Parse a cookie header according to RFC 6265.
*
* PHP will replace special characters in cookie names, which results in other cookies not being available due to
* overwriting. Thus, the server request should take the cookies from the request header instead.
*
* @param $cookieHeader
* @return array
*/
private static function parseCookieHeader($cookieHeader)
{
preg_match_all('(
(?:^\\n?[ \t]*|;[ ])
(?P<name>[!#$%&\'*+-.0-9A-Z^_`a-z|~]+)
=
(?P<DQUOTE>"?)
(?P<value>[\x21\x23-\x2b\x2d-\x3a\x3c-\x5b\x5d-\x7e]*)
(?P=DQUOTE)
(?=\\n?[ \t]*$|;[ ])
)x', $cookieHeader, $matches, PREG_SET_ORDER);

$cookies = [];

foreach ($matches as $match) {
$cookies[$match['name']] = urldecode($match['value']);
}

return $cookies;
}
}
40 changes: 40 additions & 0 deletions src/functions/create_uploaded_file.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros;

use InvalidArgumentException;

/**
* Create an uploaded file instance from an array of values.
*
* @param array $spec A single $_FILES entry.
* @return UploadedFile
* @throws InvalidArgumentException if one or more of the tmp_name, size,
* or error keys are missing from $spec.
*/
function createUploadedFile(array $spec)
{
if (! isset($spec['tmp_name'])
|| ! isset($spec['size'])
|| ! isset($spec['error'])
) {
throw new InvalidArgumentException(sprintf(
'$spec provided to %s MUST contain each of the keys "tmp_name",'
. ' "size", and "error"; one or more were missing',
__FUNCTION__
));
}

return new UploadedFile(
$spec['tmp_name'],
$spec['size'],
$spec['error'],
$spec['name'] ?? null,
$spec['type'] ?? null
);
}
26 changes: 26 additions & 0 deletions src/functions/get_header_from_array.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* @see https://github.com/zendframework/zend-diactoros for the canonical source repository
* @copyright Copyright (c) 2018 Zend Technologies USA Inc. (https://www.zend.com)
* @license https://github.com/zendframework/zend-diactoros/blob/master/LICENSE.md New BSD License
*/

namespace Zend\Diactoros;

/**
* @param string $name
* @param array $headers Array of headers
* @param mixed $default Default value to return if $name not found in $headers.
* @return mixed
*/
function getHeaderFromArray($name, array $headers, $default = null)
{
$header = strtolower($name);
$headers = array_change_key_case($headers, CASE_LOWER);
if (array_key_exists($header, $headers)) {
$value = is_array($headers[$header]) ? implode(', ', $headers[$header]) : $headers[$header];
return $value;
}

return $default;
}
Loading

0 comments on commit 51b6629

Please sign in to comment.