From b1a93dadf28703e54e02807598da472ac00c4c78 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 26 Jun 2018 11:01:41 -0500 Subject: [PATCH 1/4] Extracts all methods for marshaling uploaded files to functions Creates the following functions under the `Zend\Diactoros` namespace: - `createUploadedFile()` - `normalizeUploadedFiles()` - `normalizeUploadedFileSpecification()` The following methods are REMOVED from `ServerRequestFactory`: - `createUploadedFileFromSpec()` - `normalizeNestedFileSpec()` The method `ServerRequestFactory::normalizeFiles()` is DEPRECATED, and no longer used internally; it proxies to `normalizeUploadedFiles()`. --- composer.json | 5 ++ composer.lock | 2 +- src/ServerRequestFactory.php | 73 +------------------ src/functions/create_uploaded_file.php | 40 ++++++++++ .../normalize_uploaded_file_specification.php | 51 +++++++++++++ src/functions/normalize_uploaded_files.php | 50 +++++++++++++ test/ServerRequestFactoryTest.php | 4 +- 7 files changed, 153 insertions(+), 72 deletions(-) create mode 100644 src/functions/create_uploaded_file.php create mode 100644 src/functions/normalize_uploaded_file_specification.php create mode 100644 src/functions/normalize_uploaded_files.php diff --git a/composer.json b/composer.json index f4278091..f7a363fd 100644 --- a/composer.json +++ b/composer.json @@ -37,6 +37,11 @@ "psr/http-message-implementation": "1.0" }, "autoload": { + "files": [ + "src/functions/create_uploaded_file.php", + "src/functions/normalize_uploaded_files.php", + "src/functions/normalize_uploaded_file_specification.php" + ], "psr-4": { "Zend\\Diactoros\\": "src/" } diff --git a/composer.lock b/composer.lock index 102472cc..7bfcbe8e 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "02a4a4ab06235930ffa531039a00af20", diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 57cdc167..8c398239 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -78,7 +78,7 @@ 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)) { @@ -182,32 +182,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); } /** @@ -438,55 +420,6 @@ private static function marshalIpv6HostAndPort(stdClass $accumulator, array $ser } } - /** - * 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) * diff --git a/src/functions/create_uploaded_file.php b/src/functions/create_uploaded_file.php new file mode 100644 index 00000000..03ae13eb --- /dev/null +++ b/src/functions/create_uploaded_file.php @@ -0,0 +1,40 @@ + $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + 'name' => $files['name'][$key] ?? null, + 'type' => $files['type'][$key] ?? null, + ]; + $normalized[$key] = createUploadedFile($spec); + } + return $normalized; +} diff --git a/src/functions/normalize_uploaded_files.php b/src/functions/normalize_uploaded_files.php new file mode 100644 index 00000000..4f762670 --- /dev/null +++ b/src/functions/normalize_uploaded_files.php @@ -0,0 +1,50 @@ + $value) { + if ($value instanceof UploadedFileInterface) { + $normalized[$key] = $value; + continue; + } + + if (is_array($value) && isset($value['tmp_name']) && is_array($value['tmp_name'])) { + $normalized[$key] = normalizeUploadedFileSpecification($value); + continue; + } + + if (is_array($value) && isset($value['tmp_name'])) { + $normalized[$key] = createUploadedFile($value); + continue; + } + + if (is_array($value)) { + $normalized[$key] = normalizeUploadedFiles($value); + continue; + } + + throw new InvalidArgumentException('Invalid value in files specification'); + } + return $normalized; +} diff --git a/test/ServerRequestFactoryTest.php b/test/ServerRequestFactoryTest.php index 63f202e1..696d08b5 100644 --- a/test/ServerRequestFactoryTest.php +++ b/test/ServerRequestFactoryTest.php @@ -16,6 +16,8 @@ use Zend\Diactoros\UploadedFile; use Zend\Diactoros\Uri; +use function Zend\Diactoros\normalizeUploadedFiles; + class ServerRequestFactoryTest extends TestCase { public function testGetWillReturnValueIfPresentInArray() @@ -538,7 +540,7 @@ public function testNormalizeFilesReturnsOnlyActualFilesWhenOriginalFilesContain 'type' => ['file' => 'text/plain'], ]]; - $normalizedFiles = ServerRequestFactory::normalizeFiles($files); + $normalizedFiles = normalizeUploadedFiles($files); $this->assertCount(1, $normalizedFiles['fooFiles']); } From c4381603452112539d1b26a23c451783db8e9dd1 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 26 Jun 2018 11:07:23 -0500 Subject: [PATCH 2/4] Extracts ServerRequestFactory::marshalProtocolVersion to a function ADDS the function `Zend\Diactoros\marshalProtocolVersion`. REMOVES the method `ServerRequestFactory::marshalProtocolVersion()`. --- composer.json | 1 + src/ServerRequestFactory.php | 24 +-------------- src/functions/marshal_protocol_version.php | 34 ++++++++++++++++++++++ test/ServerRequestFactoryTest.php | 15 +++------- 4 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 src/functions/marshal_protocol_version.php diff --git a/composer.json b/composer.json index f7a363fd..e67f318c 100644 --- a/composer.json +++ b/composer.json @@ -39,6 +39,7 @@ "autoload": { "files": [ "src/functions/create_uploaded_file.php", + "src/functions/marshal_protocol_version.php", "src/functions/normalize_uploaded_files.php", "src/functions/normalize_uploaded_file_specification.php" ], diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 8c398239..5e010ddd 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -95,7 +95,7 @@ public static function fromGlobals( $cookies ?: $_COOKIE, $query ?: $_GET, $body ?: $_POST, - static::marshalProtocolVersion($server) + marshalProtocolVersion($server) ); } @@ -420,28 +420,6 @@ private static function marshalIpv6HostAndPort(stdClass $accumulator, array $ser } } - /** - * 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[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. * diff --git a/src/functions/marshal_protocol_version.php b/src/functions/marshal_protocol_version.php new file mode 100644 index 00000000..6dc6c16b --- /dev/null +++ b/src/functions/marshal_protocol_version.php @@ -0,0 +1,34 @@ +[1-9]\d*(?:\.\d)?)$#', $server['SERVER_PROTOCOL'], $matches)) { + throw new UnexpectedValueException(sprintf( + 'Unrecognized protocol version (%s)', + $server['SERVER_PROTOCOL'] + )); + } + + return $matches['version']; +} diff --git a/test/ServerRequestFactoryTest.php b/test/ServerRequestFactoryTest.php index 696d08b5..be0b843c 100644 --- a/test/ServerRequestFactoryTest.php +++ b/test/ServerRequestFactoryTest.php @@ -16,6 +16,7 @@ use Zend\Diactoros\UploadedFile; use Zend\Diactoros\Uri; +use function Zend\Diactoros\marshalProtocolVersion; use function Zend\Diactoros\normalizeUploadedFiles; class ServerRequestFactoryTest extends TestCase @@ -547,19 +548,13 @@ public function testNormalizeFilesReturnsOnlyActualFilesWhenOriginalFilesContain public function testMarshalProtocolVersionRisesExceptionIfVersionIsNotRecognized() { - $method = new ReflectionMethod(ServerRequestFactory::class, 'marshalProtocolVersion'); - $method->setAccessible(true); - $this->expectException(UnexpectedValueException::class); - - $method->invoke(null, ['SERVER_PROTOCOL' => 'dadsa/1.0']); + marshalProtocolVersion(['SERVER_PROTOCOL' => 'dadsa/1.0']); } public function testMarshalProtocolReturnsDefaultValueIfHeaderIsNotPresent() { - $method = new ReflectionMethod(ServerRequestFactory::class, 'marshalProtocolVersion'); - $method->setAccessible(true); - $version = $method->invoke(null, []); + $version = marshalProtocolVersion([]); $this->assertSame('1.1', $version); } @@ -568,9 +563,7 @@ public function testMarshalProtocolReturnsDefaultValueIfHeaderIsNotPresent() */ public function testMarshalProtocolVersionReturnsHttpVersions($protocol, $expected) { - $method = new ReflectionMethod(ServerRequestFactory::class, 'marshalProtocolVersion'); - $method->setAccessible(true); - $version = $method->invoke(null, ['SERVER_PROTOCOL' => $protocol]); + $version = marshalProtocolVersion(['SERVER_PROTOCOL' => $protocol]); $this->assertSame($expected, $version); } From 81e806f92d83e8be1c050554d0620970a2f693ee Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 26 Jun 2018 11:10:48 -0500 Subject: [PATCH 3/4] Extracts ServerRequestFactory::parseCookieHeader to a function ADDS the function `Zend\Diactoros\parseCookieHeader()`. REMOVES the function `ServerRequestFactory::parseCookieHeader()`. --- composer.json | 3 ++- src/ServerRequestFactory.php | 32 +--------------------- src/functions/parse_cookie_header.php | 38 +++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 32 deletions(-) create mode 100644 src/functions/parse_cookie_header.php diff --git a/composer.json b/composer.json index e67f318c..63a0a521 100644 --- a/composer.json +++ b/composer.json @@ -41,7 +41,8 @@ "src/functions/create_uploaded_file.php", "src/functions/marshal_protocol_version.php", "src/functions/normalize_uploaded_files.php", - "src/functions/normalize_uploaded_file_specification.php" + "src/functions/normalize_uploaded_file_specification.php", + "src/functions/parse_cookie_header.php" ], "psr-4": { "Zend\\Diactoros\\": "src/" diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 5e010ddd..5b8a33b9 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -82,7 +82,7 @@ public static function fromGlobals( $headers = static::marshalHeaders($server); if (null === $cookies && array_key_exists('cookie', $headers)) { - $cookies = self::parseCookieHeader($headers['cookie']); + $cookies = parseCookieHeader($headers['cookie']); } return new ServerRequest( @@ -419,34 +419,4 @@ private static function marshalIpv6HostAndPort(stdClass $accumulator, array $ser $accumulator->port = null; } } - - /** - * 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[!#$%&\'*+-.0-9A-Z^_`a-z|~]+) - = - (?P"?) - (?P[\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; - } } diff --git a/src/functions/parse_cookie_header.php b/src/functions/parse_cookie_header.php new file mode 100644 index 00000000..fa8de908 --- /dev/null +++ b/src/functions/parse_cookie_header.php @@ -0,0 +1,38 @@ +[!#$%&\'*+-.0-9A-Z^_`a-z|~]+) + = + (?P"?) + (?P[\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; +} From 17b49fbfa6bfb176daace9c32b32467c6fa27171 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 26 Jun 2018 12:04:23 -0500 Subject: [PATCH 4/4] Extract logic for marshaling host and port to dedicated function(s) ADDS `Zend\Diactoros\getHeaderFromArray(string $name, array $headers, $default = null)`, for extracting a value from a header array. DEPRECATES `ServerRequestFactory::getHeader()`, and has it proxy to `getHeaderFromArray()`. ADDS `Zend\Diactoros\marshalHostAndPort(array $headers, array $server) : array`, to allow detecting the host and port from either the request headers or the SAPI environment. DEPRECATES `ServerRequestFactory::marshalHostAndPortFromHeaders()`, and has it proxy to `marshalHostAndPort(). It is no longer used internally. REMOTES `ServerRequestFactory::marshalHostAndPortFromHeader()` and `ServerRequestFactory::marshalIpv6HostAndPort()`. Both were private, and are no longer called within `ServerRequestFactory`; the logic in each is now embedded in `marshalHostAndPort()`. --- composer.json | 2 + src/ServerRequestFactory.php | 81 +++---------------------- src/functions/get_header_from_array.php | 26 ++++++++ src/functions/marshal_host_and_port.php | 81 +++++++++++++++++++++++++ test/ServerRequestFactoryTest.php | 68 +++++++++++---------- 5 files changed, 154 insertions(+), 104 deletions(-) create mode 100644 src/functions/get_header_from_array.php create mode 100644 src/functions/marshal_host_and_port.php diff --git a/composer.json b/composer.json index 63a0a521..d3d76d98 100644 --- a/composer.json +++ b/composer.json @@ -39,6 +39,8 @@ "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", diff --git a/src/ServerRequestFactory.php b/src/ServerRequestFactory.php index 5b8a33b9..6e10a45b 100644 --- a/src/ServerRequestFactory.php +++ b/src/ServerRequestFactory.php @@ -127,6 +127,7 @@ 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 @@ -134,14 +135,7 @@ public static function get($key, array $values, $default = null) */ 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); } /** @@ -245,7 +239,7 @@ 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'; } @@ -253,9 +247,7 @@ public static function marshalUriFromServer(array $server, array $headers) // 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)) { @@ -288,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; } /** @@ -379,44 +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; - } - } } diff --git a/src/functions/get_header_from_array.php b/src/functions/get_header_from_array.php new file mode 100644 index 00000000..595c50ef --- /dev/null +++ b/src/functions/get_header_from_array.php @@ -0,0 +1,26 @@ +withMethod('GET'); $request = $request->withHeader('Host', 'example.com'); - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, [], $request->getHeaders()); - $this->assertSame('example.com', $accumulator->host); - $this->assertNull($accumulator->port); + list($host, $port) = marshalHostAndPort($request->getHeaders(), []); + + $this->assertSame('example.com', $host); + $this->assertNull($port); } public function testMarshalHostAndPortWillDetectPortInHostHeaderWhenPresent() @@ -191,10 +192,10 @@ public function testMarshalHostAndPortWillDetectPortInHostHeaderWhenPresent() $request = $request->withMethod('GET'); $request = $request->withHeader('Host', 'example.com:8000'); - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, [], $request->getHeaders()); - $this->assertSame('example.com', $accumulator->host); - $this->assertSame(8000, $accumulator->port); + list($host, $port) = marshalHostAndPort($request->getHeaders(), []); + + $this->assertSame('example.com', $host); + $this->assertSame(8000, $port); } public function testMarshalHostAndPortReturnsEmptyValuesIfNoHostHeaderAndNoServerName() @@ -202,10 +203,10 @@ public function testMarshalHostAndPortReturnsEmptyValuesIfNoHostHeaderAndNoServe $request = new ServerRequest(); $request = $request->withUri(new Uri()); - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, [], $request->getHeaders()); - $this->assertSame('', $accumulator->host); - $this->assertNull($accumulator->port); + list($host, $port) = marshalHostAndPort($request->getHeaders(), []); + + $this->assertSame('', $host); + $this->assertNull($port); } public function testMarshalHostAndPortReturnsServerNameForHostWhenPresent() @@ -216,10 +217,11 @@ public function testMarshalHostAndPortReturnsServerNameForHostWhenPresent() $server = [ 'SERVER_NAME' => 'example.com', ]; - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, $server, $request->getHeaders()); - $this->assertSame('example.com', $accumulator->host); - $this->assertNull($accumulator->port); + + list($host, $port) = marshalHostAndPort($request->getHeaders(), $server); + + $this->assertSame('example.com', $host); + $this->assertNull($port); } public function testMarshalHostAndPortReturnsServerPortForPortWhenPresentWithServerName() @@ -231,10 +233,11 @@ public function testMarshalHostAndPortReturnsServerPortForPortWhenPresentWithSer 'SERVER_NAME' => 'example.com', 'SERVER_PORT' => 8000, ]; - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, $server, $request->getHeaders()); - $this->assertSame('example.com', $accumulator->host); - $this->assertSame(8000, $accumulator->port); + + list($host, $port) = marshalHostAndPort($request->getHeaders(), $server); + + $this->assertSame('example.com', $host); + $this->assertSame(8000, $port); } public function testMarshalHostAndPortReturnsServerNameForHostIfServerAddrPresentButHostIsNotIpv6Address() @@ -246,9 +249,10 @@ public function testMarshalHostAndPortReturnsServerNameForHostIfServerAddrPresen 'SERVER_ADDR' => '127.0.0.1', 'SERVER_NAME' => 'example.com', ]; - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, $server, $request->getHeaders()); - $this->assertSame('example.com', $accumulator->host); + + list($host, $port) = marshalHostAndPort($request->getHeaders(), $server); + + $this->assertSame('example.com', $host); } public function testMarshalHostAndPortReturnsServerAddrForHostIfPresentAndHostIsIpv6Address() @@ -261,10 +265,11 @@ public function testMarshalHostAndPortReturnsServerAddrForHostIfPresentAndHostIs 'SERVER_NAME' => '[FE80::0202:B3FF:FE1E:8329]', 'SERVER_PORT' => 8000, ]; - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, $server, $request->getHeaders()); - $this->assertSame('[FE80::0202:B3FF:FE1E:8329]', $accumulator->host); - $this->assertSame(8000, $accumulator->port); + + list($host, $port) = marshalHostAndPort($request->getHeaders(), $server); + + $this->assertSame('[FE80::0202:B3FF:FE1E:8329]', $host); + $this->assertSame(8000, $port); } public function testMarshalHostAndPortWillDetectPortInIpv6StyleHost() @@ -276,10 +281,11 @@ public function testMarshalHostAndPortWillDetectPortInIpv6StyleHost() 'SERVER_ADDR' => 'FE80::0202:B3FF:FE1E:8329', 'SERVER_NAME' => '[FE80::0202:B3FF:FE1E:8329:80]', ]; - $accumulator = (object) ['host' => '', 'port' => null]; - ServerRequestFactory::marshalHostAndPortFromHeaders($accumulator, $server, $request->getHeaders()); - $this->assertSame('[FE80::0202:B3FF:FE1E:8329]', $accumulator->host); - $this->assertSame(80, $accumulator->port); + + list($host, $port) = marshalHostAndPort($request->getHeaders(), $server); + + $this->assertSame('[FE80::0202:B3FF:FE1E:8329]', $host); + $this->assertSame(80, $port); } public function testMarshalUriDetectsHttpsSchemeFromServerValue()