Skip to content

Commit e542f2f

Browse files
committed
feat: update error formatting to return HttpError object with status and headers
1 parent 80bfbe3 commit e542f2f

8 files changed

Lines changed: 47 additions & 31 deletions

File tree

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"friendsofphp/php-cs-fixer": "^3.10",
2424
"phpstan/phpstan": "^2.0",
2525
"phpunit/phpunit": "^10.0",
26-
"symfony/http-kernel": "^6.1|^7.2"
26+
"symfony/http-foundation": "^6.1|^7.2"
2727
},
2828
"autoload": {
2929
"psr-4": {

src/Formatter/AbstractErrorFormatter.php

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@
55
namespace Jenky\ApiError\Formatter;
66

77
use Jenky\ApiError\DebuggableProblem;
8+
use Jenky\ApiError\HttpError;
89
use Jenky\ApiError\Problem;
910
use Jenky\ApiError\Transformer\ExceptionTransformer;
11+
use Symfony\Component\ErrorHandler\Exception\FlattenException;
1012

11-
/**
12-
* @template T of array-key
13-
*/
1413
abstract class AbstractErrorFormatter implements ErrorFormatter
1514
{
1615
public function __construct(
@@ -20,16 +19,13 @@ public function __construct(
2019
}
2120

2221
/**
23-
* @return array<T, mixed> $format
22+
* @return array<string, mixed> $format
2423
*/
2524
abstract protected function getFormat(): array;
2625

2726
abstract protected function createProblem(\Throwable $exception): Problem;
2827

29-
/**
30-
* @return array<T, mixed>
31-
*/
32-
public function format(\Throwable $exception): array
28+
public function format(\Throwable $exception): HttpError
3329
{
3430
$format = $this->getFormat();
3531

@@ -47,9 +43,17 @@ public function format(\Throwable $exception): array
4743
? $problem->debugContext()
4844
: $problem->context();
4945

46+
$status = 500;
47+
$headers = [];
48+
49+
if ($problem instanceof FlattenException) {
50+
$status = $problem->getStatusCode();
51+
$headers = $problem->getHeaders();
52+
}
53+
5054
\array_walk_recursive($format, $this->replacePlaceHolder(...), $context);
5155

52-
return $this->removeEmptyPlaceHolders($format);
56+
return new HttpError($this->removeEmptyPlaceHolders($format), $status, $headers);
5357
}
5458

5559
/**
@@ -92,7 +96,7 @@ function (array $matches) use ($context, &$cache) {
9296
/**
9397
* @param array<array-key, mixed> $input
9498
*
95-
* @return array<T, mixed>
99+
* @return array<string, mixed>
96100
*/
97101
private function removeEmptyPlaceHolders(array $input): array
98102
{

src/Formatter/ErrorFormatter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
namespace Jenky\ApiError\Formatter;
66

7+
use Jenky\ApiError\HttpError;
8+
79
interface ErrorFormatter
810
{
9-
public function format(\Throwable $exception): mixed;
11+
public function format(\Throwable $exception): HttpError;
1012
}

src/Formatter/GenericErrorFormatter.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
use Jenky\ApiError\GenericProblem;
88
use Jenky\ApiError\Problem;
99

10-
/**
11-
* @extends AbstractErrorFormatter<string>
12-
*/
1310
final class GenericErrorFormatter extends AbstractErrorFormatter
1411
{
1512
protected function createProblem(\Throwable $exception): Problem

src/Formatter/Rfc7807ErrorFormatter.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77
use Jenky\ApiError\Problem;
88
use Jenky\ApiError\Rfc7807Problem;
99

10-
/**
11-
* @extends AbstractErrorFormatter<string>
12-
*/
1310
final class Rfc7807ErrorFormatter extends AbstractErrorFormatter
1411
{
1512
protected function createProblem(\Throwable $exception): Problem

src/Handler/Symfony/JsonResponseHandler.php

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
use Symfony\Component\HttpFoundation\JsonResponse;
99
use Symfony\Component\HttpFoundation\Request;
1010
use Symfony\Component\HttpFoundation\Response;
11-
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
1211

1312
if (! class_exists(Request::class) || ! class_exists(Response::class)) {
1413
throw new \LogicException('You cannot use the "Jenky\ApiError\Handler\Symfony\JsonResponseHandler" as the "symfony/http-foundation" package is not installed. Try running "composer require symfony/http-foundation".');
@@ -31,18 +30,12 @@ public function render(\Throwable $exception, Request $request): ?JsonResponse
3130
return null;
3231
}
3332

34-
$status = 500;
35-
$headers = [];
36-
37-
if (interface_exists(HttpExceptionInterface::class) && $exception instanceof HttpExceptionInterface) {
38-
$status = $exception->getStatusCode();
39-
$headers = $exception->getHeaders();
40-
}
33+
$data = $this->formatter->format($exception);
4134

4235
return new JsonResponse(
43-
$this->formatter->format($exception),
44-
$status,
45-
\array_merge(['Content-Type' => $this->contentType], $headers)
36+
$data,
37+
$data->statusCode,
38+
\array_merge(['Content-Type' => $this->contentType], $data->headers)
4639
);
4740
}
4841

src/HttpError.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Jenky\ApiError;
6+
7+
final class HttpError implements \JsonSerializable
8+
{
9+
/**
10+
* @param array<string, mixed> $headers
11+
*/
12+
public function __construct(
13+
public readonly mixed $data,
14+
public readonly int $statusCode = 500,
15+
public readonly array $headers = [],
16+
) {
17+
}
18+
19+
public function jsonSerialize(): mixed
20+
{
21+
return $this->data;
22+
}
23+
}

tests/ErrorFormatterTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public function test_generic_error_formatter(\Throwable $exception, bool $debug)
1616
{
1717
$formatter = new GenericErrorFormatter($debug);
1818

19-
$data = $formatter->format($exception);
19+
$data = $formatter->format($exception)->data;
2020

2121
$this->assertIsArray($data);
2222
$this->assertArrayHasKey('message', $data);
@@ -39,7 +39,7 @@ public function test_rfc7808_error_formatter(\Throwable $exception, bool $debug)
3939
{
4040
$formatter = new Rfc7807ErrorFormatter($debug);
4141

42-
$data = $formatter->format($exception);
42+
$data = $formatter->format($exception)->data;
4343

4444
$this->assertIsArray($data);
4545
$this->assertArrayHasKey('type', $data);

0 commit comments

Comments
 (0)