ResponseResolverChain::completeResponse()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
c 0
b 0
f 0
nc 4
nop 2
dl 0
loc 19
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
1
<?php
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sunrise\Http\Router;
15
16
use InvalidArgumentException;
17
use LogicException;
18
use Psr\Http\Message\ResponseInterface;
19
use Psr\Http\Message\ServerRequestInterface;
20
use ReflectionAttribute;
21
use ReflectionMethod;
22
use Sunrise\Http\Router\Annotation\ResponseHeader;
23
use Sunrise\Http\Router\Annotation\ResponseStatus;
24
25
use function sprintf;
26
use function usort;
27
28
/**
29
 * @since 3.0.0
30
 */
31
final class ResponseResolverChain implements ResponseResolverChainInterface
32
{
33
    private bool $isSorted = false;
34
35 9
    public function __construct(
36
        /** @var array<array-key, ResponseResolverInterface> */
37
        private array $resolvers = [],
38
    ) {
39 9
    }
40
41
    /**
42
     * @inheritDoc
43
     *
44
     * @throws InvalidArgumentException
45
     * @throws LogicException
46
     */
47 6
    public function resolveResponse(
48
        mixed $response,
49
        ReflectionMethod $responder,
50
        ServerRequestInterface $request,
51
    ): ResponseInterface {
52 6
        if ($response instanceof ResponseInterface) {
53 2
            return $response;
54
        }
55
56 5
        $this->isSorted or $this->sortResolvers();
57 5
        foreach ($this->resolvers as $resolver) {
58 4
            $result = $resolver->resolveResponse($response, $responder, $request);
59 4
            if ($result instanceof ResponseInterface) {
60 3
                return self::completeResponse($result, $responder);
61
            }
62
        }
63
64 2
        throw new LogicException(sprintf(
65 2
            'The responder "%s" returned an unsupported response that cannot be resolved.',
66 2
            self::stringifyResponder($responder),
67 2
        ));
68
    }
69
70 3
    private static function completeResponse(
71
        ResponseInterface $response,
72
        ReflectionMethod $responder,
73
    ): ResponseInterface {
74
        /** @var list<ReflectionAttribute<ResponseStatus>> $annotations */
75 3
        $annotations = $responder->getAttributes(ResponseStatus::class);
76 3
        if (isset($annotations[0])) {
77 1
            $status = $annotations[0]->newInstance();
78 1
            $response = $response->withStatus($status->code, $status->phrase);
79
        }
80
81
        /** @var list<ReflectionAttribute<ResponseHeader>> $annotations */
82 3
        $annotations = $responder->getAttributes(ResponseHeader::class);
83 3
        foreach ($annotations as $annotation) {
84 1
            $header = $annotation->newInstance();
85 1
            $response = $response->withHeader($header->name, $header->value);
86
        }
87
88 3
        return $response;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $response could return the type Psr\Http\Message\MessageInterface which includes types incompatible with the type-hinted return Psr\Http\Message\ResponseInterface. Consider adding an additional type-check to rule them out.
Loading history...
89
    }
90
91 5
    private function sortResolvers(): void
92
    {
93 5
        $this->isSorted = usort($this->resolvers, static fn(
94 5
            ResponseResolverInterface $a,
95 5
            ResponseResolverInterface $b,
96 5
        ): int => $b->getWeight() <=> $a->getWeight());
97
    }
98
99 3
    public static function stringifyResponder(ReflectionMethod $responder): string
100
    {
101 3
        return sprintf('%s::%s()', $responder->class, $responder->name);
102
    }
103
}
104