From 2dec061a585de6d82f1cfd9c9e362bacc024f7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A1gi-Kaz=C3=A1r=20M=C3=A1rk?= Date: Sun, 20 Dec 2015 12:45:37 +0100 Subject: [PATCH] Add message decorators --- spec/Decorator/MessageDecoratorSpec.php | 142 +++++++++++++++++++++++ spec/Decorator/RequestDecoratorSpec.php | 108 +++++++++++++++++ spec/Decorator/ResponseDecoratorSpec.php | 84 ++++++++++++++ src/Decorator/MessageDecorator.php | 133 +++++++++++++++++++++ src/Decorator/RequestDecorator.php | 88 ++++++++++++++ src/Decorator/ResponseDecorator.php | 57 +++++++++ 6 files changed, 612 insertions(+) create mode 100644 spec/Decorator/MessageDecoratorSpec.php create mode 100644 spec/Decorator/RequestDecoratorSpec.php create mode 100644 spec/Decorator/ResponseDecoratorSpec.php create mode 100644 src/Decorator/MessageDecorator.php create mode 100644 src/Decorator/RequestDecorator.php create mode 100644 src/Decorator/ResponseDecorator.php diff --git a/spec/Decorator/MessageDecoratorSpec.php b/spec/Decorator/MessageDecoratorSpec.php new file mode 100644 index 0000000..53f79ec --- /dev/null +++ b/spec/Decorator/MessageDecoratorSpec.php @@ -0,0 +1,142 @@ +beAnInstanceOf('spec\Http\Message\Decorator\MessageDecoratorStub', [$message]); + } + + function it_is_initializable() + { + $this->shouldHaveType('spec\Http\Message\Decorator\MessageDecoratorStub'); + } + + function it_is_a_message() + { + $this->shouldImplement('Psr\Http\Message\MessageInterface'); + } + + function it_is_a_message_decorator() + { + $this->shouldUseTrait('Http\Message\Decorator\MessageDecorator'); + } + + function it_has_a_message() + { + $this->getMessage()->shouldImplement('Psr\Http\Message\MessageInterface'); + } + + function it_has_a_protocol_version(MessageInterface $message) + { + $message->getProtocolVersion()->willReturn('1.1'); + + $this->getProtocolVersion()->shouldReturn('1.1'); + } + + function it_accepts_a_protocol_version(MessageInterface $message, MessageInterface $newMessage) + { + $message->withProtocolVersion('1.1')->willReturn($newMessage); + + $new = $this->withProtocolVersion('1.1'); + $new->getMessage()->shouldReturn($newMessage); + } + + function it_has_headers(MessageInterface $message) + { + $headers = [ + 'Content-Type' => 'application/xml' + ]; + + $message->getHeaders()->willReturn($headers); + + $this->getHeaders()->shouldReturn($headers); + } + + function it_can_check_a_header(MessageInterface $message) + { + $message->hasHeader('Content-Type')->willReturn(true); + + $this->hasHeader('Content-Type')->shouldReturn(true); + } + + function it_has_a_header(MessageInterface $message) + { + $message->getHeader('Content-Type')->willReturn('application/xml'); + + $this->getHeader('Content-Type')->shouldReturn('application/xml'); + } + + function it_has_a_header_line(MessageInterface $message) + { + $message->getHeaderLine('Accept-Encoding')->willReturn('gzip, deflate'); + + $this->getHeaderLine('Accept-Encoding')->shouldReturn('gzip, deflate'); + } + + function it_accepts_a_header(MessageInterface $message, MessageInterface $newMessage) + { + $message->withHeader('Content-Type', 'application/xml')->willReturn($newMessage); + + $new = $this->withHeader('Content-Type', 'application/xml'); + $new->getMessage()->shouldReturn($newMessage); + } + + function it_accepts_added_headers(MessageInterface $message, MessageInterface $newMessage) + { + $message->withAddedHeader('Content-Type', 'application/xml')->willReturn($newMessage); + + $new = $this->withAddedHeader('Content-Type', 'application/xml'); + $new->getMessage()->shouldReturn($newMessage); + } + + function it_removes_a_header(MessageInterface $message, MessageInterface $newMessage) + { + $message->withoutHeader('Content-Type')->willReturn($newMessage); + + $new = $this->withoutHeader('Content-Type'); + $new->getMessage()->shouldReturn($newMessage); + } + + function it_has_a_body(MessageInterface $message, StreamInterface $body) + { + $message->getBody()->willReturn($body); + + $this->getBody()->shouldReturn($body); + } + + function it_accepts_a_body(MessageInterface $message, MessageInterface $newMessage, StreamInterface $body) + { + $message->withBody($body)->willReturn($newMessage); + + $new = $this->withBody($body); + $new->getMessage()->shouldReturn($newMessage); + } + + function getMatchers() + { + return [ + 'useTrait' => function ($subject, $trait) { + return class_uses($subject, $trait); + } + ]; + } +} + +class MessageDecoratorStub implements MessageInterface +{ + use MessageDecorator; + + public function __construct(MessageInterface $message) + { + $this->message = $message; + } +} diff --git a/spec/Decorator/RequestDecoratorSpec.php b/spec/Decorator/RequestDecoratorSpec.php new file mode 100644 index 0000000..776cb94 --- /dev/null +++ b/spec/Decorator/RequestDecoratorSpec.php @@ -0,0 +1,108 @@ +beAnInstanceOf('spec\Http\Message\Decorator\RequestDecoratorStub', [$request]); + } + + function it_is_initializable() + { + $this->shouldHaveType('spec\Http\Message\Decorator\RequestDecoratorStub'); + } + + function it_is_a_request() + { + $this->shouldImplement('Psr\Http\Message\RequestInterface'); + } + + function it_is_a_request_decorator() + { + $this->shouldUseTrait('Http\Message\Decorator\RequestDecorator'); + } + + function it_has_a_request() + { + $this->getRequest()->shouldImplement('Psr\Http\Message\RequestInterface'); + } + + function it_accepts_a_request(RequestInterface $request) + { + $new = $this->withRequest($request); + + $new->getRequest()->shouldReturn($request); + } + + function it_has_a_request_target(RequestInterface $request) + { + $request->getRequestTarget()->willReturn('/'); + + $this->getRequestTarget()->shouldReturn('/'); + } + + function it_accepts_a_request_target(RequestInterface $request, RequestInterface $newRequest) + { + $request->withRequestTarget('/')->willReturn($newRequest); + + $new = $this->withRequestTarget('/'); + $new->getMessage()->shouldReturn($newRequest); + } + + function it_has_a_method(RequestInterface $request) + { + $request->getMethod()->willReturn('GET'); + + $this->getMethod()->shouldReturn('GET'); + } + + function it_accepts_a_method(RequestInterface $request, RequestInterface $newRequest) + { + $request->withMethod('GET')->willReturn($newRequest); + + $new = $this->withMethod('GET'); + $new->getMessage()->shouldReturn($newRequest); + } + + function it_has_an_uri(RequestInterface $request, UriInterface $uri) + { + $request->getUri()->willReturn($uri); + + $this->getUri()->shouldReturn($uri); + } + + function it_accepts_an_uri(RequestInterface $request, RequestInterface $newRequest, UriInterface $uri) + { + $request->withUri($uri, false)->willReturn($newRequest); + + $new = $this->withUri($uri); + $new->getMessage()->shouldReturn($newRequest); + } + + function getMatchers() + { + return [ + 'useTrait' => function ($subject, $trait) { + return class_uses($subject, $trait); + } + ]; + } +} + +class RequestDecoratorStub implements RequestInterface +{ + use RequestDecorator; + + public function __construct(RequestInterface $request) + { + $this->message = $request; + } +} diff --git a/spec/Decorator/ResponseDecoratorSpec.php b/spec/Decorator/ResponseDecoratorSpec.php new file mode 100644 index 0000000..7fdd61c --- /dev/null +++ b/spec/Decorator/ResponseDecoratorSpec.php @@ -0,0 +1,84 @@ +beAnInstanceOf('spec\Http\Message\Decorator\ResponseDecoratorStub', [$response]); + } + + function it_is_initializable() + { + $this->shouldHaveType('spec\Http\Message\Decorator\ResponseDecoratorStub'); + } + + function it_is_a_response() + { + $this->shouldImplement('Psr\Http\Message\ResponseInterface'); + } + + function it_is_a_response_decorator() + { + $this->shouldUseTrait('Http\Message\Decorator\ResponseDecorator'); + } + + function it_has_a_response() + { + $this->getResponse()->shouldImplement('Psr\Http\Message\ResponseInterface'); + } + + function it_accepts_a_response(ResponseInterface $response) + { + $new = $this->withResponse($response); + + $new->getResponse()->shouldReturn($response); + } + + function it_has_a_status_code(ResponseInterface $response) + { + $response->getStatusCode()->willReturn(200); + + $this->getStatusCode()->shouldReturn(200); + } + + function it_accepts_a_status(ResponseInterface $response, ResponseInterface $newResponse) + { + $response->withStatus(200, 'OK')->willReturn($newResponse); + + $new = $this->withStatus(200, 'OK'); + $new->getMessage()->shouldReturn($newResponse); + } + + function it_has_a_reason_phrase(ResponseInterface $response) + { + $response->getReasonPhrase()->willReturn('OK'); + + $this->getReasonPhrase()->shouldReturn('OK'); + } + + function getMatchers() + { + return [ + 'useTrait' => function ($subject, $trait) { + return class_uses($subject, $trait); + } + ]; + } +} + +class ResponseDecoratorStub implements ResponseInterface +{ + use ResponseDecorator; + + public function __construct(ResponseInterface $response) + { + $this->message = $response; + } +} diff --git a/src/Decorator/MessageDecorator.php b/src/Decorator/MessageDecorator.php new file mode 100644 index 0000000..0ffc7ca --- /dev/null +++ b/src/Decorator/MessageDecorator.php @@ -0,0 +1,133 @@ + + */ +trait MessageDecorator +{ + /** + * @var MessageInterface + */ + private $message; + + /** + * Returns the decorated message. + * + * Since the underlying Message is immutable as well + * exposing it is not an issue, because it's state cannot be altered + * + * @return MessageInterface + */ + public function getMessage() + { + return $this->message; + } + + /** + * {@inheritdoc} + */ + public function getProtocolVersion() + { + return $this->message->getProtocolVersion(); + } + + /** + * {@inheritdoc} + */ + public function withProtocolVersion($version) + { + $new = clone $this; + $new->message = $this->message->withProtocolVersion($version); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getHeaders() + { + return $this->message->getHeaders(); + } + + /** + * {@inheritdoc} + */ + public function hasHeader($header) + { + return $this->message->hasHeader($header); + } + + /** + * {@inheritdoc} + */ + public function getHeader($header) + { + return $this->message->getHeader($header); + } + + /** + * {@inheritdoc} + */ + public function getHeaderLine($header) + { + return $this->message->getHeaderLine($header); + } + + /** + * {@inheritdoc} + */ + public function withHeader($header, $value) + { + $new = clone $this; + $new->message = $this->message->withHeader($header, $value); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withAddedHeader($header, $value) + { + $new = clone $this; + $new->message = $this->message->withAddedHeader($header, $value); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function withoutHeader($header) + { + $new = clone $this; + $new->message = $this->message->withoutHeader($header); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getBody() + { + return $this->message->getBody(); + } + + /** + * {@inheritdoc} + */ + public function withBody(StreamInterface $body) + { + $new = clone $this; + $new->message = $this->message->withBody($body); + + return $new; + } +} diff --git a/src/Decorator/RequestDecorator.php b/src/Decorator/RequestDecorator.php new file mode 100644 index 0000000..7c50e58 --- /dev/null +++ b/src/Decorator/RequestDecorator.php @@ -0,0 +1,88 @@ + + */ +trait RequestDecorator +{ + use MessageDecorator { + getMessage as getRequest; + } + + /** + * Exchanges the underlying request with another. + * + * @param RequestInterface $request + * + * @return self + */ + public function withRequest(RequestInterface $request) + { + $new = clone $this; + $new->message = $request; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getRequestTarget() + { + return $this->message->getRequestTarget(); + } + + /** + * {@inheritdoc} + */ + public function withRequestTarget($requestTarget) + { + $new = clone $this; + $new->message = $this->message->withRequestTarget($requestTarget); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getMethod() + { + return $this->message->getMethod(); + } + + /** + * {@inheritdoc} + */ + public function withMethod($method) + { + $new = clone $this; + $new->message = $this->message->withMethod($method); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getUri() + { + return $this->message->getUri(); + } + + /** + * {@inheritdoc} + */ + public function withUri(UriInterface $uri, $preserveHost = false) + { + $new = clone $this; + $new->message = $this->message->withUri($uri, $preserveHost); + + return $new; + } +} diff --git a/src/Decorator/ResponseDecorator.php b/src/Decorator/ResponseDecorator.php new file mode 100644 index 0000000..82d9ae0 --- /dev/null +++ b/src/Decorator/ResponseDecorator.php @@ -0,0 +1,57 @@ + + */ +trait ResponseDecorator +{ + use MessageDecorator { + getMessage as getResponse; + } + + /** + * Exchanges the underlying response with another. + * + * @param ResponseInterface $response + * + * @return self + */ + public function withResponse(ResponseInterface $response) + { + $new = clone $this; + $new->message = $response; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getStatusCode() + { + return $this->message->getStatusCode(); + } + + /** + * {@inheritdoc} + */ + public function withStatus($code, $reasonPhrase = '') + { + $new = clone $this; + $new->message = $this->message->withStatus($code, $reasonPhrase); + + return $new; + } + + /** + * {@inheritdoc} + */ + public function getReasonPhrase() + { + return $this->message->getReasonPhrase(); + } +}