diff --git a/spec/Builder/ResponseBuilderSpec.php b/spec/Builder/ResponseBuilderSpec.php new file mode 100644 index 0000000..606884a --- /dev/null +++ b/spec/Builder/ResponseBuilderSpec.php @@ -0,0 +1,35 @@ +beConstructedWith($response); + $this->shouldHaveType('Http\Message\Builder\ResponseBuilder'); + } + + function it_reads_headers_from_array(ResponseInterface $response) + { + $response->withStatus(200, 'OK')->willReturn($response); + $response->withProtocolVersion('1.1')->willReturn($response); + $response->hasHeader('Content-type')->willReturn(false); + $response->withHeader('Content-type', 'text/html')->willReturn($response); + $this->beConstructedWith($response); + $this->setHeadersFromArray(['HTTP/1.1 200 OK', 'Content-type: text/html']); + } + + function it_reads_headers_from_string(ResponseInterface $response) + { + $response->withStatus(200, 'OK')->willReturn($response); + $response->withProtocolVersion('1.1')->willReturn($response); + $response->hasHeader('Content-type')->willReturn(false); + $response->withHeader('Content-type', 'text/html')->willReturn($response); + $this->beConstructedWith($response); + $this->setHeadersFromString("HTTP/1.1 200 OK\r\nContent-type: text/html\r\n"); + } +} diff --git a/src/Builder/ResponseBuilder.php b/src/Builder/ResponseBuilder.php new file mode 100644 index 0000000..b841c03 --- /dev/null +++ b/src/Builder/ResponseBuilder.php @@ -0,0 +1,116 @@ +response = $response; + } + + /** + * Return response. + * + * @return ResponseInterface + */ + public function getResponse() + { + return $this->response; + } + + /** + * Add headers represented by an array of header lines. + * + * @param string[] $headers Response headers as array of header lines. + * + * @return $this + * + * @throws \UnexpectedValueException For invalid header values. + * @throws \InvalidArgumentException For invalid status code arguments. + */ + public function setHeadersFromArray(array $headers) + { + $statusLine = trim(array_shift($headers)); + $parts = explode(' ', $statusLine, 3); + if (count($parts) < 2 || substr(strtolower($parts[0]), 0, 5) !== 'http/') { + throw new \UnexpectedValueException( + sprintf('"%s" is not a valid HTTP status line', $statusLine) + ); + } + + $reasonPhrase = count($parts) > 2 ? $parts[2] : ''; + $this->response = $this->response + ->withStatus((int) $parts[1], $reasonPhrase) + ->withProtocolVersion(substr($parts[0], 5)); + + foreach ($headers as $headerLine) { + $headerLine = trim($headerLine); + if ('' === $headerLine) { + continue; + } + + $parts = explode(':', $headerLine, 2); + if (count($parts) !== 2) { + throw new \UnexpectedValueException( + sprintf('"%s" is not a valid HTTP header line', $headerLine) + ); + } + $name = trim(urldecode($parts[0])); + $value = trim(urldecode($parts[1])); + if ($this->response->hasHeader($name)) { + $this->response = $this->response->withAddedHeader($name, $value); + } else { + $this->response = $this->response->withHeader($name, $value); + } + } + + return $this; + } + + /** + * Add headers represented by a single string. + * + * @param string $headers Response headers as single string. + * + * @return $this + * + * @throws \InvalidArgumentException if $headers is not a string on object with __toString() + * @throws \UnexpectedValueException For invalid header values. + */ + public function setHeadersFromString($headers) + { + if (!(is_string($headers) + || (is_object($headers) && method_exists($headers, '__toString'))) + ) { + throw new \InvalidArgumentException( + sprintf( + '%s expects parameter 1 to be a string, %s given', + __METHOD__, + is_object($headers) ? get_class($headers) : gettype($headers) + ) + ); + } + + $this->setHeadersFromArray(explode("\r\n", $headers)); + + return $this; + } +}