Skip to content

Commit c75da34

Browse files
committed
Add ContentTypeAwarePayload and ContentAwareEncoder
Introduce a generic DTO that pairs any payload with a content type, and a decorator encoder that wraps any existing encoder to set the Content-Type header from the payload metadata.
1 parent 0a83c7d commit c75da34

4 files changed

Lines changed: 102 additions & 0 deletions

File tree

docs/transports.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ This package contains some frequently used encoders / decoders for you:
4242
| `RawDecoder` | `DecoderInterface<string>` | Returns the raw PSR-7 body string as response result |
4343
| `ResourceStreamEncoder` | `EncoderInterface<ResourceStream>` | Adds `phpro/resource-stream` as request body |
4444
| `ResourceStreamDecoder` | `DecoderInterface<ResourceStream>` | Returns `phpro/resource-stream` from response body |
45+
| `ContentTypeAwareEncoder` | `EncoderInterface<ContentTypeAwarePayload<T>>` | Decorator that wraps any encoder and sets the Content-Type header from the payload |
4546
| `ResponseDecoder` | `DecoderInterface<ResponseInterface>` | Returns the received PSR-7 response as result |
4647

4748
## Built-in transport presets:
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phpro\HttpTools\Encoding\ContentType;
6+
7+
use Phpro\HttpTools\Encoding\EncoderInterface;
8+
use Psr\Http\Message\RequestInterface;
9+
10+
/**
11+
* @template T
12+
*
13+
* @implements EncoderInterface<ContentTypeAwarePayload<T>>
14+
*/
15+
final class ContentTypeAwareEncoder implements EncoderInterface
16+
{
17+
/**
18+
* @param EncoderInterface<T> $encoder
19+
*/
20+
public function __construct(
21+
private EncoderInterface $encoder,
22+
) {
23+
}
24+
25+
/**
26+
* @param ContentTypeAwarePayload<T> $data
27+
*/
28+
public function __invoke(RequestInterface $request, $data): RequestInterface
29+
{
30+
$request = ($this->encoder)($request, $data->payload);
31+
32+
return $request->withHeader('Content-Type', $data->contentType);
33+
}
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phpro\HttpTools\Encoding\ContentType;
6+
7+
/**
8+
* @template T
9+
*/
10+
final readonly class ContentTypeAwarePayload
11+
{
12+
/**
13+
* @param T $payload
14+
*/
15+
public function __construct(
16+
public string $contentType,
17+
public mixed $payload,
18+
) {
19+
}
20+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phpro\HttpTools\Tests\Unit\Encoding\ContentType;
6+
7+
use Phpro\HttpTools\Encoding\ContentType\ContentTypeAwareEncoder;
8+
use Phpro\HttpTools\Encoding\ContentType\ContentTypeAwarePayload;
9+
use Phpro\HttpTools\Encoding\Raw\RawEncoder;
10+
use Phpro\HttpTools\Test\UseHttpFactories;
11+
use PHPUnit\Framework\Attributes\Test;
12+
use PHPUnit\Framework\TestCase;
13+
14+
final class ContentTypeAwareEncoderTest extends TestCase
15+
{
16+
use UseHttpFactories;
17+
18+
#[Test]
19+
public function it_can_encode_with_content_type(): void
20+
{
21+
$encoder = new ContentTypeAwareEncoder(
22+
RawEncoder::createWithAutodiscoveredPsrFactories()
23+
);
24+
$request = $this->createRequest('POST', '/hello');
25+
$payload = new ContentTypeAwarePayload('application/pdf', 'raw-content');
26+
27+
$actual = $encoder($request, $payload);
28+
29+
self::assertSame('raw-content', (string) $actual->getBody());
30+
self::assertSame('application/pdf', $actual->getHeaderLine('Content-Type'));
31+
}
32+
33+
#[Test]
34+
public function it_overrides_content_type_set_by_inner_encoder(): void
35+
{
36+
$encoder = new ContentTypeAwareEncoder(
37+
RawEncoder::createWithAutodiscoveredPsrFactories()
38+
);
39+
$request = $this->createRequest('POST', '/hello')
40+
->withHeader('Content-Type', 'text/plain');
41+
$payload = new ContentTypeAwarePayload('application/xml', 'raw-content');
42+
43+
$actual = $encoder($request, $payload);
44+
45+
self::assertSame('application/xml', $actual->getHeaderLine('Content-Type'));
46+
}
47+
}

0 commit comments

Comments
 (0)