Passed
Pull Request — 1.x (#293)
by Akihito
14:58
created

EmbedInterceptor::linkSelf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 2
dl 0
loc 5
ccs 0
cts 0
cp 0
crap 6
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 uri_template;
18
19
final class EmbedInterceptor implements MethodInterceptor
20
{
21
    private const SELF_LINK = '_self';
22
23
    private readonly ResourceInterface $resource;
24
25
    public function __construct(
26 9
        ResourceInterface $resource,
27
    ) {
28 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...
29 9
    }
30 9
31
    /**
32
     * {@inheritDoc}
33
     *
34
     * @throws EmbedException
35
     */
36
    public function invoke(MethodInvocation $invocation)
37 9
    {
38
        $ro = $invocation->getThis();
39
        assert($ro instanceof ResourceObject);
40 9
        $query = $this->getArgsByInvocation($invocation);
41 9
        $embeds = $invocation->getMethod()->getAnnotations();
42 9
        $this->embedResource($embeds, $ro, $query);
43 9
44 9
        return $invocation->proceed();
45
    }
46 7
47
    /**
48
     * @param array<Embed|object>  $embeds
49
     * @param array<string, mixed> $query
50
     *
51
     * @throws EmbedException
52
     *
53
     * @psalm-suppress NoInterfaceProperties
54 9
     * @psalm-suppress MixedMethodCall
55
     */
56 9
    private function embedResource(array $embeds, ResourceObject $ro, array $query): void
57
    {
58 9
        foreach ($embeds as $embed) {
59 5
            if (! $embed instanceof Embed) {
60
                continue;
61
            }
62 9
63 9
            try {
64 9
                $templateUri = $this->getFullUri($embed->src, $ro);
65 2
                $uri = uri_template($templateUri, $query);
66
                /** @var Request $request */ // phpcs:ignore SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion
67 9
                $request = $this->resource->get->uri($uri);
68
                if ($ro->body === null) {
69
                    $ro->body = [];
70 7
                }
71
72 9
                if (! is_array($ro->body)) {
73
                    throw new LinkException($embed->rel); // @codeCoverageIgnore
74 9
                }
75 1
76
                if ($embed->rel === self::SELF_LINK) {
77
                    $this->linkSelf($request, $ro);
78 9
79
                    continue;
80
                }
81 9
82
                $ro->body[$embed->rel] = clone $request;
83 9
            } catch (BadRequestException $e) {
84 9
                // wrap ResourceNotFound or Uri exception
85 9
                throw new EmbedException($embed->src, 500, $e);
86 9
            }
87 9
        }
88
    }
89
90 9
    private function getFullUri(string $uri, ResourceObject $ro): string
91
    {
92
        if ($uri[0] === '/') {
93
            $uri = "{$ro->uri->scheme}://{$ro->uri->host}" . $uri;
94
        }
95
96
        return $uri;
97
    }
98
99
    /** @return array<string, mixed> */
100
    private function getArgsByInvocation(MethodInvocation $invocation): array
101
    {
102
        /** @var list<scalar> $args */
103
        $args = $invocation->getArguments()->getArrayCopy();
104
        $params = $invocation->getMethod()->getParameters();
105
        $namedParameters = [];
106
        foreach ($params as $param) {
107
            $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

107
            $namedParameters[$param->name] = array_shift(/** @scrutinizer ignore-type */ $args);
Loading history...
108
        }
109
110
        return $namedParameters;
111
    }
112
113
    public function linkSelf(Request $request, ResourceObject $ro): void
114
    {
115
        $result = $request();
116
        foreach ($result as $key => $value) {
117
            $ro->body[$key] = $value;
118
        }
119
    }
120
}
121