Completed
Push — 1.x ( 9d9002...75031f )
by Akihito
21s queued 16s
created

EmbedInterceptor   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 4
Bugs 1 Features 1
Metric Value
eloc 43
c 4
b 1
f 1
dl 0
loc 113
ccs 32
cts 32
cp 1
rs 10
wmc 16

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A invoke() 0 9 1
A linkSelf() 0 12 2
A embedResource() 0 26 5
A getFullUri() 0 7 2
A prepareBody() 0 8 3
A getArgsByInvocation() 0 11 2
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 9
    public function __construct(
27
        ResourceInterface $resource,
28 9
    ) {
29 9
        $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 9
    }
31
32
    /**
33
     * {@inheritDoc}
34
     *
35
     * @throws EmbedException
36
     */
37 9
    public function invoke(MethodInvocation $invocation)
38
    {
39
        $ro = $invocation->getThis();
40 9
        assert($ro instanceof ResourceObject);
41 9
        $query = $this->getArgsByInvocation($invocation);
42 9
        $embeds = $invocation->getMethod()->getAnnotations();
43 9
        $this->embedResource($embeds, $ro, $query);
44 9
45
        return $invocation->proceed();
46 7
    }
47
48
    /**
49
     * @param array<Embed|object>  $embeds
50
     * @param array<string, mixed> $query
51
     *
52
     * @throws EmbedException
53
     *
54 9
     * @psalm-suppress NoInterfaceProperties
55
     * @psalm-suppress MixedMethodCall
56 9
     */
57
    private function embedResource(array $embeds, ResourceObject $ro, array $query): void
58 9
    {
59 5
        foreach ($embeds as $embed) {
60
            if (! $embed instanceof Embed) {
61
                continue;
62 9
            }
63 9
64 9
            try {
65 2
                $templateUri = $this->getFullUri($embed->src, $ro);
66
                $uri = uri_template($templateUri, $query);
67 9
                /** @var Request $request */ // phpcs:ignore SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion
68
                $request = $this->resource->get->uri($uri);
69
                $this->prepareBody($ro, $embed);
70 7
71
                if ($embed->rel === self::SELF_LINK) {
72 9
                    $this->linkSelf($request, $ro);
73
74 9
                    continue;
75 1
                }
76
77
                assert(is_array($ro->body));
78 9
79
                $ro->body[$embed->rel] = clone $request;
80
            } catch (BadRequestException $e) {
81 9
                // wrap ResourceNotFound or Uri exception
82
                throw new EmbedException($embed->src, 500, $e);
83 9
            }
84 9
        }
85 9
    }
86 9
87 9
    private function getFullUri(string $uri, ResourceObject $ro): string
88
    {
89
        if ($uri[0] === '/') {
90 9
            $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