From dca9c894763d21c67719d575533f1a8c8b38e9b5 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Wed, 24 Jun 2020 17:47:25 +0000 Subject: [PATCH] Allow raw formatter returning complete PSR 7 response --- src/BodyOnlyFormatter.php | 22 ++++++++++++++++++++++ src/ContentFormatter.php | 20 ++++++++++++++++++++ src/ContentTypeMiddleware.php | 23 ++++++++--------------- src/Formatter.php | 8 ++++---- src/Formatter/JmsSerializer.php | 6 +++--- src/Formatter/Json.php | 6 +++--- src/Formatter/Plates.php | 6 +++--- src/Formatter/StringCast.php | 6 +++--- src/Formatter/Twig.php | 6 +++--- src/NotAcceptableFormatter.php | 18 ++++++++++++++++++ tests/Formatter/JmsSerializerTest.php | 11 +++++++++-- tests/Formatter/NaiveTemplateEngine.php | 6 +++--- 12 files changed, 99 insertions(+), 39 deletions(-) create mode 100644 src/BodyOnlyFormatter.php create mode 100644 src/ContentFormatter.php create mode 100644 src/NotAcceptableFormatter.php diff --git a/src/BodyOnlyFormatter.php b/src/BodyOnlyFormatter.php new file mode 100644 index 00000000..61932984 --- /dev/null +++ b/src/BodyOnlyFormatter.php @@ -0,0 +1,22 @@ +withBody($this->formatBody($response, $streamFactory)); + } + + abstract protected function formatBody(UnformattedResponse $response, StreamFactoryInterface $streamFactory): StreamInterface; +} diff --git a/src/ContentFormatter.php b/src/ContentFormatter.php new file mode 100644 index 00000000..5e4c1003 --- /dev/null +++ b/src/ContentFormatter.php @@ -0,0 +1,20 @@ +createStream( + $this->formatContent($response->getUnformattedContent(), $response->getAttributes()) + ); + } + + abstract protected function formatContent($content, array $attributes = []): string; +} diff --git a/src/ContentTypeMiddleware.php b/src/ContentTypeMiddleware.php index e4da09d5..b2b4a63c 100644 --- a/src/ContentTypeMiddleware.php +++ b/src/ContentTypeMiddleware.php @@ -3,7 +3,6 @@ namespace Lcobucci\ContentNegotiation; -use Fig\Http\Message\StatusCodeInterface; use Middlewares\ContentType; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; @@ -18,6 +17,8 @@ final class ContentTypeMiddleware implements MiddlewareInterface private MiddlewareInterface $negotiator; private StreamFactoryInterface $streamFactory; + public const NOT_ACCEPTABLE = 'NOT ACCEPTABLE'; + /** * @var Formatter[] */ @@ -66,9 +67,9 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } $contentType = $this->extractContentType($response->getHeaderLine('Content-Type')); - $formatter = $this->formatters[$contentType] ?? null; + $formatter = $this->formatters[$contentType] ?? $this->getNotAcceptableFormatter(); - return $this->formatResponse($response, $formatter); + return $formatter->format($response, $this->streamFactory); } private function extractContentType(string $contentType): string @@ -82,20 +83,12 @@ private function extractContentType(string $contentType): string return substr($contentType, 0, $charsetSeparatorPosition); } - /** - * @throws ContentCouldNotBeFormatted - */ - private function formatResponse(UnformattedResponse $response, ?Formatter $formatter): ResponseInterface + private function getNotAcceptableFormatter(): Formatter { - if ($formatter === null) { - return $response->withBody($this->streamFactory->createStream()) - ->withStatus(StatusCodeInterface::STATUS_NOT_ACCEPTABLE); + if (empty($this->formatters[self::NOT_ACCEPTABLE])) { + $this->formatters[self::NOT_ACCEPTABLE] = new NotAcceptableFormatter(); } - return $response->withBody( - $this->streamFactory->createStream( - $formatter->format($response->getUnformattedContent(), $response->getAttributes()) - ) - ); + return $this->formatters[self::NOT_ACCEPTABLE]; } } diff --git a/src/Formatter.php b/src/Formatter.php index d4ed94a7..ad1afce5 100644 --- a/src/Formatter.php +++ b/src/Formatter.php @@ -3,13 +3,13 @@ namespace Lcobucci\ContentNegotiation; +use Psr\Http\Message\ResponseInterface; +use Psr\Http\Message\StreamFactoryInterface; + interface Formatter { /** - * @param mixed $content - * @param mixed[] $attributes - * * @throws ContentCouldNotBeFormatted */ - public function format($content, array $attributes = []): string; + public function format(UnformattedResponse $response, StreamFactoryInterface $streamFactory): ResponseInterface; } diff --git a/src/Formatter/JmsSerializer.php b/src/Formatter/JmsSerializer.php index d414d16d..398f9329 100644 --- a/src/Formatter/JmsSerializer.php +++ b/src/Formatter/JmsSerializer.php @@ -5,11 +5,11 @@ use JMS\Serializer\SerializerInterface; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use Throwable; use function sprintf; -final class JmsSerializer implements Formatter +final class JmsSerializer extends ContentFormatter { private SerializerInterface $serializer; private string $format; @@ -23,7 +23,7 @@ public function __construct(SerializerInterface $serializer, string $format) /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { try { return $this->serializer->serialize($content, $this->format); diff --git a/src/Formatter/Json.php b/src/Formatter/Json.php index e8887bcc..0ecc7693 100644 --- a/src/Formatter/Json.php +++ b/src/Formatter/Json.php @@ -4,7 +4,7 @@ namespace Lcobucci\ContentNegotiation\Formatter; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use Throwable; use function json_encode; use function sprintf; @@ -15,7 +15,7 @@ use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; -final class Json implements Formatter +final class Json extends ContentFormatter { private const DEFAULT_FLAGS = JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_SLASHES; @@ -29,7 +29,7 @@ public function __construct(int $flags = self::DEFAULT_FLAGS) /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { try { return json_encode($content, $this->flags | JSON_THROW_ON_ERROR); diff --git a/src/Formatter/Plates.php b/src/Formatter/Plates.php index 80a14d76..8a90bc8b 100644 --- a/src/Formatter/Plates.php +++ b/src/Formatter/Plates.php @@ -4,11 +4,11 @@ namespace Lcobucci\ContentNegotiation\Formatter; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use League\Plates\Engine; use Throwable; -final class Plates implements Formatter +final class Plates extends ContentFormatter { private const DEFAULT_ATTRIBUTE = 'template'; @@ -24,7 +24,7 @@ public function __construct(Engine $engine, string $attributeName = self::DEFAUL /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { try { return $this->render($content, $attributes); diff --git a/src/Formatter/StringCast.php b/src/Formatter/StringCast.php index 22a70f01..841935eb 100644 --- a/src/Formatter/StringCast.php +++ b/src/Formatter/StringCast.php @@ -4,16 +4,16 @@ namespace Lcobucci\ContentNegotiation\Formatter; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use function is_object; use function method_exists; -final class StringCast implements Formatter +final class StringCast extends ContentFormatter { /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { if (is_object($content) && ! method_exists($content, '__toString')) { throw new ContentCouldNotBeFormatted('Given data could not be cast to string'); diff --git a/src/Formatter/Twig.php b/src/Formatter/Twig.php index 4018b15c..63fd8765 100644 --- a/src/Formatter/Twig.php +++ b/src/Formatter/Twig.php @@ -4,11 +4,11 @@ namespace Lcobucci\ContentNegotiation\Formatter; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use Throwable; use Twig\Environment; -final class Twig implements Formatter +final class Twig extends ContentFormatter { private const DEFAULT_ATTRIBUTE = 'template'; @@ -26,7 +26,7 @@ public function __construct( /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { try { return $this->render($content, $attributes); diff --git a/src/NotAcceptableFormatter.php b/src/NotAcceptableFormatter.php new file mode 100644 index 00000000..632c3137 --- /dev/null +++ b/src/NotAcceptableFormatter.php @@ -0,0 +1,18 @@ +withBody($streamFactory->createStream()) + ->withStatus(StatusCodeInterface::STATUS_NOT_ACCEPTABLE); + } +} diff --git a/tests/Formatter/JmsSerializerTest.php b/tests/Formatter/JmsSerializerTest.php index d3f269f1..52cb144c 100644 --- a/tests/Formatter/JmsSerializerTest.php +++ b/tests/Formatter/JmsSerializerTest.php @@ -4,8 +4,11 @@ namespace Lcobucci\ContentNegotiation\Tests\Formatter; use JMS\Serializer\SerializerInterface; +use Laminas\Diactoros\Response; +use Laminas\Diactoros\StreamFactory; use Lcobucci\ContentNegotiation\ContentCouldNotBeFormatted; use Lcobucci\ContentNegotiation\Formatter\JmsSerializer; +use Lcobucci\ContentNegotiation\UnformattedResponse; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use RuntimeException; @@ -37,6 +40,7 @@ public function createSerializer(): void public function formatShouldSimplyForwardCallToSerializer(): void { $content = ['a' => 'test']; + $unformatted = new UnformattedResponse(new Response(), $content); $this->serializer->expects(self::once()) ->method('serialize') @@ -45,7 +49,9 @@ public function formatShouldSimplyForwardCallToSerializer(): void $formatter = new JmsSerializer($this->serializer, 'json'); - self::assertSame('{"a":"test"}', $formatter->format($content)); + $response = $formatter->format($unformatted, new StreamFactory()); + + self::assertSame('{"a":"test"}', $response->getBody()->getContents()); } /** @@ -62,6 +68,7 @@ public function formatShouldConvertAnyRaisedException(): void ->willThrowException(new RuntimeException()); $formatter = new JmsSerializer($this->serializer, 'json'); - $formatter->format(['a' => 'test']); + $unformatted = new UnformattedResponse(new Response(), ['a' => 'test']); + $formatter->format($unformatted, new StreamFactory()); } } diff --git a/tests/Formatter/NaiveTemplateEngine.php b/tests/Formatter/NaiveTemplateEngine.php index 5cc9b428..9ce9c345 100644 --- a/tests/Formatter/NaiveTemplateEngine.php +++ b/tests/Formatter/NaiveTemplateEngine.php @@ -3,7 +3,7 @@ namespace Lcobucci\ContentNegotiation\Tests\Formatter; -use Lcobucci\ContentNegotiation\Formatter; +use Lcobucci\ContentNegotiation\ContentFormatter; use SplFileObject; use function array_keys; use function array_map; @@ -12,7 +12,7 @@ use function str_replace; use function trim; -final class NaiveTemplateEngine implements Formatter +final class NaiveTemplateEngine extends ContentFormatter { private const BASE_DIR = __DIR__ . '/../../templates/naive/'; private const EXTENSION = 'html'; @@ -20,7 +20,7 @@ final class NaiveTemplateEngine implements Formatter /** * {@inheritdoc} */ - public function format($content, array $attributes = []): string + public function formatContent($content, array $attributes = []): string { $template = $this->getTemplateContent($attributes);