Passed
Push — master ( 1b664c...c4ed35 )
by Mr
02:12
created

ActionHandler::executeAction()   A

Complexity

Conditions 5
Paths 28

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 25
nc 28
nop 2
dl 0
loc 37
ccs 0
cts 34
cp 0
crap 30
rs 9.2088
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * This file is part of the daikon-cqrs/boot project.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Daikon\Boot\Middleware;
10
11
use Daikon\Boot\Middleware\Action\ActionInterface;
12
use Daikon\Boot\Middleware\Action\ResponderInterface;
13
use Daikon\Boot\Middleware\Action\ValidatorInterface;
14
use Daikon\Interop\Assertion;
15
use Daikon\Interop\AssertionFailedException;
16
use Daikon\Interop\RuntimeException;
17
use Exception;
18
use Fig\Http\Message\StatusCodeInterface;
19
use Psr\Container\ContainerInterface;
20
use Psr\Http\Message\ResponseInterface;
21
use Psr\Http\Message\ServerRequestInterface;
22
use Psr\Http\Server\MiddlewareInterface;
23
use Psr\Http\Server\RequestHandlerInterface;
24
use Psr\Log\LoggerInterface;
25
26
class ActionHandler implements MiddlewareInterface, StatusCodeInterface
27
{
28
    use ResolvesDependency;
29
30
    public const ATTR_STATUS_CODE = '_status_code';
31
    public const ATTR_ERRORS = '_errors';
32
    public const ATTR_ERROR_SEVERITY = '_error_severity';
33
    public const ATTR_RESPONDER = '_responder';
34
    public const ATTR_VALIDATOR = '_validator';
35
    public const ATTR_PAYLOAD = '_payload';
36
37
    protected ContainerInterface $container;
38
39
    protected LoggerInterface $logger;
40
41
    public function __construct(ContainerInterface $container, LoggerInterface $logger)
42
    {
43
        $this->container = $container;
44
        $this->logger = $logger;
45
    }
46
47
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
48
    {
49
        $requestHandler = $request->getAttribute(RoutingHandler::ATTR_REQUEST_HANDLER);
50
        return $requestHandler instanceof ActionInterface
51
            ? $this->executeAction($requestHandler, $request)
52
            : $handler->handle($request);
53
    }
54
55
    protected function executeAction(ActionInterface $action, ServerRequestInterface $request): ResponseInterface
56
    {
57
        try {
58
            $request = $action->registerValidator($request);
59
            if ($validator = $this->getValidator($request)) {
60
                $request = $validator($request);
61
                Assertion::noContent($request->getAttribute(self::ATTR_ERRORS));
62
            }
63
64
            $request = $action($request);
65
        } catch (Exception $error) {
66
            switch (true) {
67
                case $error instanceof AssertionFailedException:
68
                    $statusCode = self::STATUS_UNPROCESSABLE_ENTITY;
69
                    break;
70
                default:
71
                    $this->logger->error($error->getMessage(), ['trace' => $error->getTrace()]);
72
                    $statusCode = self::STATUS_INTERNAL_SERVER_ERROR;
73
            }
74
            $request = $action->handleError(
75
                $request->withAttribute(
76
                    self::ATTR_STATUS_CODE,
77
                    $request->getAttribute(self::ATTR_STATUS_CODE, $statusCode)
78
                )->withAttribute(
79
                    self::ATTR_ERRORS,
80
                    $request->getAttribute(self::ATTR_ERRORS, $error)
81
                )
82
            );
83
        }
84
85
        if (!$responder = $this->getResponder($request)) {
86
            throw $error ?? new RuntimeException(
87
                sprintf("Unable to determine responder for '%s'.", get_class($action))
88
            );
89
        }
90
91
        return $responder($request);
92
    }
93
94
    protected function getValidator(ServerRequestInterface $request): ?ValidatorInterface
95
    {
96
        $validator = $request->getAttribute(self::ATTR_VALIDATOR);
97
        if ($validator) {
98
            /** @var ValidatorInterface $validator */
99
            $validator = $this->resolve($this->container, $validator, ValidatorInterface::class);
100
        }
101
        return $validator;
102
    }
103
104
    protected function getResponder(ServerRequestInterface $request): ?ResponderInterface
105
    {
106
        $responder = $request->getAttribute(self::ATTR_RESPONDER);
107
        if ($responder) {
108
            /** @var ResponderInterface $responder */
109
            $responder = $this->resolve($this->container, $responder, ResponderInterface::class);
110
        }
111
        return $responder;
112
    }
113
}
114