Skip to content

Commit

Permalink
Allow raw formatter returning complete PSR 7 response
Browse files Browse the repository at this point in the history
  • Loading branch information
hiqsol committed Jun 25, 2020
1 parent e30be10 commit 8ae16b7
Show file tree
Hide file tree
Showing 16 changed files with 120 additions and 67 deletions.
22 changes: 22 additions & 0 deletions src/BodyOnlyFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace Lcobucci\ContentNegotiation;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

abstract class BodyOnlyFormatter implements Formatter
{
/**
* @throws ContentCouldNotBeFormatted
*/
public function format(UnformattedResponse $response, StreamFactoryInterface $streamFactory): ResponseInterface
{
return $response->withBody($this->formatBody($response, $streamFactory));
}

abstract protected function formatBody(UnformattedResponse $response, StreamFactoryInterface $streamFactory): StreamInterface;
}
20 changes: 20 additions & 0 deletions src/ContentFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace Lcobucci\ContentNegotiation;

use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

abstract class ContentFormatter extends BodyOnlyFormatter
{
protected function formatBody(UnformattedResponse $response, StreamFactoryInterface $streamFactory): StreamInterface
{
return $streamFactory->createStream(
$this->formatContent($response->getUnformattedContent(), $response->getAttributes())
);
}

abstract protected function formatContent($content, array $attributes = []): string;
}
23 changes: 8 additions & 15 deletions src/ContentTypeMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,6 +17,8 @@ final class ContentTypeMiddleware implements MiddlewareInterface
private MiddlewareInterface $negotiator;
private StreamFactoryInterface $streamFactory;

public const NOT_ACCEPTABLE = 'NOT ACCEPTABLE';

/**
* @var Formatter[]
*/
Expand Down Expand Up @@ -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
Expand All @@ -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];
}
}
8 changes: 4 additions & 4 deletions src/Formatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
6 changes: 3 additions & 3 deletions src/Formatter/JmsSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/Formatter/Json.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/Formatter/Plates.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/Formatter/StringCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
6 changes: 3 additions & 3 deletions src/Formatter/Twig.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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);
Expand Down
18 changes: 18 additions & 0 deletions src/NotAcceptableFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Lcobucci\ContentNegotiation;

use Fig\Http\Message\StatusCodeInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;

class NotAcceptableFormatter implements Formatter
{
public function format(UnformattedResponse $response, StreamFactoryInterface $streamFactory): ResponseInterface
{
return $response->withBody($streamFactory->createStream())
->withStatus(StatusCodeInterface::STATUS_NOT_ACCEPTABLE);
}
}
4 changes: 2 additions & 2 deletions tests/Formatter/JmsSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function formatShouldSimplyForwardCallToSerializer(): void

$formatter = new JmsSerializer($this->serializer, 'json');

self::assertSame('{"a":"test"}', $formatter->format($content));
self::assertSame('{"a":"test"}', $formatter->formatContent($content));
}

/**
Expand All @@ -62,6 +62,6 @@ public function formatShouldConvertAnyRaisedException(): void
->willThrowException(new RuntimeException());

$formatter = new JmsSerializer($this->serializer, 'json');
$formatter->format(['a' => 'test']);
$formatter->formatContent(['a' => 'test']);
}
}
24 changes: 12 additions & 12 deletions tests/Formatter/JsonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ final class JsonTest extends TestCase
*
* @covers ::__construct()
*
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::format()
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::formatContent()
*/
public function constructorShouldAllowTheConfigurationOfEncodingFlags(): void
{
self::assertSame(
'["<foo>","\'bar\'","\"baz\"","&blong&","\u00e9","http://"]',
(new Json(JSON_UNESCAPED_SLASHES))->format(['<foo>', "'bar'", '"baz"', '&blong&', "\xc3\xa9", 'http://'])
(new Json(JSON_UNESCAPED_SLASHES))->formatContent(['<foo>', "'bar'", '"baz"', '&blong&', "\xc3\xa9", 'http://'])
);
}

Expand All @@ -37,35 +37,35 @@ public function constructorShouldAllowTheConfigurationOfEncodingFlags(): void
*
* @covers ::__construct()
*
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::format()
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::formatContent()
*/
public function constructorShouldUseDefaultFlagsWhenNothingWasSet(): void
{
self::assertSame(
'["\u003Cfoo\u003E","\u0027bar\u0027","\u0022baz\u0022","\u0026blong\u0026","\u00e9","http://"]',
$this->format(['<foo>', "'bar'", '"baz"', '&blong&', "\xc3\xa9", 'http://'])
$this->formatContent(['<foo>', "'bar'", '"baz"', '&blong&', "\xc3\xa9", 'http://'])
);
}

/**
* @test
*
* @covers ::format()
* @covers ::formatContent()
*
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::__construct()
*/
public function formatShouldReturnAJsonEncodedValue(): void
{
self::assertJsonStringEqualsJsonString(
'{"id":1,"name":"Test"}',
$this->format(new PersonDto(1, 'Test'))
$this->formatContent(new PersonDto(1, 'Test'))
);
}

/**
* @test
*
* @covers ::format()
* @covers ::formatContent()
*
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::__construct()
*/
Expand All @@ -74,13 +74,13 @@ public function formatShouldRaiseExceptionWhenContentCouldNotBeEncoded(): void
$this->expectException(ContentCouldNotBeFormatted::class);
$this->expectExceptionMessage('Inf and NaN cannot be JSON encoded');

$this->format(acos(8));
$this->formatContent(acos(8));
}

/**
* @test
*
* @covers ::format()
* @covers ::formatContent()
*
* @uses \Lcobucci\ContentNegotiation\Formatter\Json::__construct()
*/
Expand All @@ -89,7 +89,7 @@ public function formatShouldConvertAnyExceptionDuringJsonSerialization(): void
$this->expectException(ContentCouldNotBeFormatted::class);
$this->expectExceptionMessage('An exception was thrown during JSON formatting');

$this->format(
$this->formatContent(
new class implements JsonSerializable
{
public function jsonSerialize(): void
Expand All @@ -103,10 +103,10 @@ public function jsonSerialize(): void
/**
* @param mixed $content
*/
private function format($content): string
private function formatContent($content): string
{
$formatter = new Json();

return $formatter->format($content);
return $formatter->formatContent($content);
}
}
6 changes: 3 additions & 3 deletions tests/Formatter/NaiveTemplateEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -12,15 +12,15 @@
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';

/**
* {@inheritdoc}
*/
public function format($content, array $attributes = []): string
public function formatContent($content, array $attributes = []): string
{
$template = $this->getTemplateContent($attributes);

Expand Down
Loading

0 comments on commit 8ae16b7

Please sign in to comment.