EmbedInterceptor::invoke()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use BEAR\Resource\Annotation\Embed;
8
use BEAR\Resource\Exception\BadRequestException;
9
use BEAR\Resource\Exception\EmbedException;
10
use BEAR\Resource\Exception\LinkException;
11
use Ray\Aop\MethodInterceptor;
12
use Ray\Aop\MethodInvocation;
13
14
use function array_shift;
15
use function assert;
16
use function is_array;
17
use function is_string;
18
use function uri_template;
19
20
final class EmbedInterceptor implements MethodInterceptor
21
{
22
    private const SELF_LINK = '_self';
23
24
    private readonly ResourceInterface $resource;
25
26
    public function __construct(
27
        ResourceInterface $resource,
28
    ) {
29
        $this->resource = clone $resource;
0 ignored issues
show
Bug introduced by
The property resource is declared read-only in BEAR\Resource\EmbedInterceptor.
Loading history...
30
    }
31
32
    /**
33
     * {@inheritDoc}
34
     *
35
     * @throws EmbedException
36
     */
37
    public function invoke(MethodInvocation $invocation)
38
    {
39
        $ro = $invocation->getThis();
40
        assert($ro instanceof ResourceObject);
41
        $query = $this->getArgsByInvocation($invocation);
42
        $embeds = $invocation->getMethod()->getAnnotations();
43
        $this->embedResource($embeds, $ro, $query);
44
45
        return $invocation->proceed();
46
    }
47
48
    /**
49
     * @param array<Embed|object>  $embeds
50
     * @param array<string, mixed> $query
51
     *
52
     * @throws EmbedException
53
     *
54
     * @psalm-suppress NoInterfaceProperties
55
     * @psalm-suppress MixedMethodCall
56
     */
57
    private function embedResource(array $embeds, ResourceObject $ro, array $query): void
58
    {
59
        foreach ($embeds as $embed) {
60
            if (! $embed instanceof Embed) {
61
                continue;
62
            }
63
64
            try {
65
                $templateUri = $this->getFullUri($embed->src, $ro);
66
                $uri = uri_template($templateUri, $query);
67
                /** @var Request $request */ // phpcs:ignore SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion
68
                $request = $this->resource->get->uri($uri);
69
                $this->prepareBody($ro, $embed);
70
71
                if ($embed->rel === self::SELF_LINK) {
72
                    $this->linkSelf($request, $ro);
73
74
                    continue;
75
                }
76
77
                assert(is_array($ro->body));
78
79
                $ro->body[$embed->rel] = clone $request;
80
            } catch (BadRequestException $e) {
81
                // wrap ResourceNotFound or Uri exception
82
                throw new EmbedException($embed->src, 500, $e);
83
            }
84
        }
85
    }
86
87
    private function getFullUri(string $uri, ResourceObject $ro): string
88
    {
89
        if ($uri[0] === '/') {
90
            $uri = "{$ro->uri->scheme}://{$ro->uri->host}" . $uri;
91
        }
92
93
        return $uri;
94
    }
95
96
    public function prepareBody(ResourceObject $ro, Embed $embed): void
97
    {
98
        if ($ro->body === null) {
99
            $ro->body = [];
100
        }
101
102
        if (! is_array($ro->body)) {
103
            throw new LinkException($embed->rel); // @codeCoverageIgnore
104
        }
105
    }
106
107
    /** @return array<string, mixed> */
108
    private function getArgsByInvocation(MethodInvocation $invocation): array
109
    {
110
        /** @var list<scalar> $args */
111
        $args = $invocation->getArguments()->getArrayCopy();
112
        $params = $invocation->getMethod()->getParameters();
113
        $namedParameters = [];
114
        foreach ($params as $param) {
115
            $namedParameters[$param->name] = array_shift($args);
0 ignored issues
show
Bug introduced by
$args of type BEAR\Resource\list is incompatible with the type array expected by parameter $array of array_shift(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
            $namedParameters[$param->name] = array_shift(/** @scrutinizer ignore-type */ $args);
Loading history...
116
        }
117
118
        return $namedParameters;
119
    }
120
121
    public function linkSelf(Request $request, ResourceObject $ro): void
122
    {
123
        $result = $request();
124
        assert(is_array($result->body));
125
        /** @var mixed $value */
126
        foreach ($result->body as $key => $value) {
127
            assert(is_string($key));
128
            /** @psalm-suppress MixedArrayAssignment */
129
            $ro->body[$key] = $value; // @phpstan-ignore-line
130
        }
131
132
        $ro->code = $result->code;
133
    }
134
}
135