Skip to content

Commit 4ecf1c5

Browse files
committed
Merge branch 'master' into add-error-dispatching
# Conflicts: # src/Middleware/ErrorCatcher.php
2 parents 85684ec + 7ebbf4e commit 4ecf1c5

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 3.0.1 under development
44

55
- Chg #75: Dispatch `ApplicationError` in `ErrorCatcher` (@xepozz)
6+
- Enh #82: Add `HeadersProvider` (@xepozz)
67

78
## 3.0.0 February 14, 2023
89

src/HeadersProvider.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Yiisoft\ErrorHandler;
6+
7+
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
8+
9+
/**
10+
* `HeadersProvider` provides headers for error response.
11+
* It is used by {@see ErrorCatcher} to add headers to response in case of error.
12+
*/
13+
final class HeadersProvider
14+
{
15+
/**
16+
* @param array<string, string[]> $headers Default headers list.
17+
*/
18+
public function __construct(
19+
private array $headers = [],
20+
) {
21+
}
22+
23+
/**
24+
* Adds a header to the list of headers.
25+
*
26+
* @param string $name The header name.
27+
* @param string|string[] $values The header value.
28+
*/
29+
public function add(string $name, string|array $values): void
30+
{
31+
$this->headers[$name] = (array)$values;
32+
}
33+
34+
/**
35+
* Returns all headers.
36+
*
37+
* @return array<string, string[]> The headers list.
38+
*/
39+
public function getAll(): array
40+
{
41+
return $this->headers;
42+
}
43+
}

src/Middleware/ErrorCatcher.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Throwable;
1616
use Yiisoft\ErrorHandler\Event\ApplicationError;
1717
use Yiisoft\ErrorHandler\ErrorHandler;
18+
use Yiisoft\ErrorHandler\HeadersProvider;
1819
use Yiisoft\ErrorHandler\Renderer\HeaderRenderer;
1920
use Yiisoft\ErrorHandler\Renderer\HtmlRenderer;
2021
use Yiisoft\ErrorHandler\Renderer\JsonRenderer;
@@ -39,6 +40,8 @@
3940
*/
4041
final class ErrorCatcher implements MiddlewareInterface
4142
{
43+
private HeadersProvider $headersProvider;
44+
4245
/**
4346
* @psalm-var array<string,class-string<ThrowableRendererInterface>>
4447
*/
@@ -57,7 +60,9 @@ public function __construct(
5760
private ErrorHandler $errorHandler,
5861
private ContainerInterface $container,
5962
private ?EventDispatcherInterface $eventDispatcher = null,
63+
HeadersProvider $headersProvider = null,
6064
) {
65+
$this->headersProvider = $headersProvider ?? new HeadersProvider();
6166
}
6267

6368
/**
@@ -143,7 +148,9 @@ private function generateErrorResponse(Throwable $t, ServerRequestInterface $req
143148

144149
$data = $this->errorHandler->handle($t, $renderer, $request);
145150
$response = $this->responseFactory->createResponse(Status::INTERNAL_SERVER_ERROR);
146-
151+
foreach ($this->headersProvider->getAll() as $name => $value) {
152+
$response = $response->withHeader($name, $value);
153+
}
147154
return $data->addToResponse($response->withHeader(Header::CONTENT_TYPE, $contentType));
148155
}
149156

tests/Middleware/ErrorCatcherTest.php

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
use Psr\Http\Server\RequestHandlerInterface;
1515
use Psr\Log\LoggerInterface;
1616
use RuntimeException;
17-
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
1817
use Yiisoft\ErrorHandler\ErrorHandler;
18+
use Yiisoft\ErrorHandler\HeadersProvider;
19+
use Yiisoft\ErrorHandler\Middleware\ErrorCatcher;
1920
use Yiisoft\ErrorHandler\Renderer\HeaderRenderer;
2021
use Yiisoft\ErrorHandler\Renderer\PlainTextRenderer;
2122
use Yiisoft\ErrorHandler\ThrowableRendererInterface;
@@ -229,16 +230,50 @@ public function testForceContentTypeSetInvalidType(): void
229230
->forceContentType('image/gif');
230231
}
231232

232-
private function createErrorHandler(): ErrorHandler
233+
public function testAddedHeaders(): void
233234
{
234-
$logger = $this->createMock(LoggerInterface::class);
235-
return new ErrorHandler($logger, new PlainTextRenderer());
235+
$provider = new HeadersProvider([
236+
'X-Default' => 'default',
237+
'Content-Type' => 'incorrect',
238+
]);
239+
$provider->add('X-Test', 'test');
240+
$provider->add('X-Test2', ['test2', 'test3']);
241+
$catcher = $this
242+
->createErrorCatcher(provider: $provider)
243+
->withRenderer('*/*', PlainTextRenderer::class);
244+
$response = $catcher->process(
245+
$this->createServerRequest('GET', ['Accept' => ['test/test']]),
246+
$this->createRequestHandlerWithThrowable(),
247+
);
248+
$headers = $response->getHeaders();
249+
250+
$this->assertArrayHasKey('Content-Type', $headers);
251+
$this->assertNotEquals('incorrect', $headers['Content-Type']);
252+
253+
$this->assertArrayHasKey('X-Default', $headers);
254+
$this->assertEquals(['default'], $headers['X-Default']);
255+
$this->assertArrayHasKey('X-Test', $headers);
256+
$this->assertEquals(['test'], $headers['X-Test']);
257+
$this->assertArrayHasKey('X-Test2', $headers);
258+
$this->assertEquals(['test2', 'test3'], $headers['X-Test2']);
236259
}
237260

238-
private function createErrorCatcher(): ErrorCatcher
239-
{
261+
private function createErrorCatcher(
262+
HeadersProvider $provider = null,
263+
): ErrorCatcher {
240264
$container = new SimpleContainer([], fn (string $className): object => new $className());
241-
return new ErrorCatcher(new ResponseFactory(), $this->createErrorHandler(), $container);
265+
return new ErrorCatcher(
266+
new ResponseFactory(),
267+
$this->createErrorHandler(),
268+
$container,
269+
$provider ?? new HeadersProvider()
270+
);
271+
}
272+
273+
private function createErrorHandler(): ErrorHandler
274+
{
275+
$logger = $this->createMock(LoggerInterface::class);
276+
return new ErrorHandler($logger, new PlainTextRenderer());
242277
}
243278

244279
private function createServerRequest(string $method, array $headers = []): ServerRequestInterface

0 commit comments

Comments
 (0)