Completed
Push — master ( fcdf89...4a0ecc )
by Alexander
14:58
created

ErrorCatcher::getContentType()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 9
rs 10
c 1
b 0
f 0
1
<?php
2
namespace Yiisoft\Yii\Web\Middleware;
3
4
use Psr\Container\ContainerInterface;
5
use Psr\Http\Message\ResponseFactoryInterface;
6
use Psr\Http\Message\ResponseInterface;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Server\MiddlewareInterface;
9
use Psr\Http\Server\RequestHandlerInterface;
10
use Yiisoft\Yii\Web\ErrorHandler\ErrorHandler;
11
use Yiisoft\Yii\Web\ErrorHandler\ThrowableRendererInterface;
12
use Yiisoft\Yii\Web\ErrorHandler\HtmlRenderer;
13
use Yiisoft\Yii\Web\ErrorHandler\JsonRenderer;
14
use Yiisoft\Yii\Web\ErrorHandler\PlainTextRenderer;
15
use Yiisoft\Yii\Web\ErrorHandler\XmlRenderer;
16
17
/**
18
 * ErrorCatcher catches all throwables from the next middlewares and renders it
19
 * accoring to the content type passed by the client.
20
 */
21
class ErrorCatcher implements MiddlewareInterface
22
{
23
    private $responseFactory;
24
    private $errorHandler;
25
    private $container;
26
27
    private $renderers = [
28
        'application/json' => JsonRenderer::class,
29
        'application/xml' => XmlRenderer::class,
30
        'text/xml' => XmlRenderer::class,
31
        'text/plain' => PlainTextRenderer::class,
32
        'text/html' => HtmlRenderer::class,
33
    ];
34
35
    public function __construct(ResponseFactoryInterface $responseFactory, ErrorHandler $errorHandler, ContainerInterface $container)
36
    {
37
        $this->responseFactory = $responseFactory;
38
        $this->errorHandler = $errorHandler;
39
        $this->container = $container;
40
    }
41
42
    private function handleException(\Throwable $e, ServerRequestInterface $request): ResponseInterface
43
    {
44
        $contentType = $this->getContentType($request);
45
        $content = $this->errorHandler->handleCaughtThrowable($e, $this->getRenderer($contentType));
46
47
        $response = $this->responseFactory->createResponse(500)
48
            ->withHeader('Content-type', $contentType);
49
        $response->getBody()->write($content);
50
        return $response;
51
    }
52
53
    private function getRenderer(string $contentType): ?ThrowableRendererInterface
54
    {
55
        if (isset($this->renderers[$contentType])) {
56
            return $this->container->get($this->renderers[$contentType]);
57
        }
58
59
        return null;
60
    }
61
62
    private function getContentType(ServerRequestInterface $request): string
63
    {
64
        $acceptHeaders = preg_split('~\s*,\s*~', $request->getHeaderLine('Accept'), PREG_SPLIT_NO_EMPTY);
65
        foreach ($acceptHeaders as $header) {
66
            if (array_key_exists($header, $this->renderers)) {
67
                return $header;
68
            }
69
        }
70
        return 'text/html';
71
    }
72
73
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
74
    {
75
        try {
76
            return $handler->handle($request);
77
        } catch (\Throwable $e) {
78
            return $this->handleException($e, $request);
79
        }
80
    }
81
}
82