ContentBasedErrorResponseGenerator::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 5
cts 5
cp 1
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 4
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Acelaya\ExpressiveErrorHandler\ErrorHandler;
6
7
use Acelaya\ExpressiveErrorHandler\Exception\InvalidArgumentException;
8
use Acelaya\ExpressiveErrorHandler\Log\LogMessageBuilderInterface;
9
use Psr\Http\Message\ResponseInterface as Response;
10
use Psr\Http\Message\ServerRequestInterface as Request;
11
use Psr\Log\LoggerInterface;
12
use Throwable;
13
14
use function explode;
15
use function implode;
16
use function sprintf;
17
18
class ContentBasedErrorResponseGenerator implements ErrorResponseGeneratorInterface
19
{
20
    private const DEFAULT_CONTENT = 'text/html';
21
22
    /** @var ErrorResponseGeneratorManagerInterface */
23
    private $errorHandlerManager;
24
    /** @var LoggerInterface */
25
    private $logger;
26
    /** @var LogMessageBuilderInterface */
27
    private $logMessageBuilder;
28
    /** @var string */
29
    private $defaultContentType;
30
31
    /**
32
     * ContentBasedErrorResponseGenerator constructor.
33
     * @param ErrorResponseGeneratorManagerInterface $errorHandlerManager
34
     * @param LoggerInterface $logger
35
     * @param LogMessageBuilderInterface $logMessageBuilder
36
     * @param string $defaultContentType
37
     */
38 7
    public function __construct(
39
        ErrorResponseGeneratorManagerInterface $errorHandlerManager,
40
        LoggerInterface $logger,
41
        LogMessageBuilderInterface $logMessageBuilder,
42
        string $defaultContentType = self::DEFAULT_CONTENT
43
    ) {
44 7
        $this->errorHandlerManager = $errorHandlerManager;
45 7
        $this->logger = $logger;
46 7
        $this->logMessageBuilder = $logMessageBuilder;
47 7
        $this->defaultContentType = $defaultContentType;
48
    }
49
50
    /**
51
     * Final handler for an application.
52
     *
53
     * @param \Throwable|null $e
54
     * @param Request $request
55
     * @param Response $response
56
     * @return Response
57
     * @throws InvalidArgumentException
58
     */
59 5
    public function __invoke(?Throwable $e, Request $request, Response $response): Response
60
    {
61
        // Try to get an error handler for provided request accepted type
62 5
        $errorHandler = $this->resolveErrorHandlerFromAcceptHeader($request);
63 4
        $this->logger->error($this->logMessageBuilder->buildMessage($request, $response, $e));
64 4
        return $errorHandler($e, $request, $response);
65
    }
66
67
    /**
68
     * Tries to resolve an error response generator based on request's accept header
69
     *
70
     * @param Request $request
71
     * @return callable
72
     * @throws InvalidArgumentException
73
     */
74 5
    private function resolveErrorHandlerFromAcceptHeader(Request $request): callable
75
    {
76
        // Try to find an error handler for one of the accepted content types
77 5
        $accepts = $request->hasHeader('Accept') ? $request->getHeaderLine('Accept') : $this->defaultContentType;
78
        /** @var array $accepts */
79 5
        $accepts = explode(',', $accepts);
80 5
        foreach ($accepts as $accept) {
81 5
            if (! $this->errorHandlerManager->has($accept)) {
82 4
                continue;
83
            }
84
85 2
            return $this->errorHandlerManager->get($accept);
86
        }
87
88
        // If it wasn't possible to find an error handler for accepted content type, use default one if registered
89 3
        if ($this->errorHandlerManager->has($this->defaultContentType)) {
90 2
            return $this->errorHandlerManager->get($this->defaultContentType);
91
        }
92
93
        // It wasn't possible to find an error handler
94 1
        throw new InvalidArgumentException(sprintf(
95
            'It wasn\'t possible to find an error handler for ["%s"] content types. '
96 1
            . 'Make sure you have registered at least the default "%s" content type',
97 1
            implode('", "', $accepts),
98 1
            $this->defaultContentType
99
        ));
100
    }
101
}
102