EmbedInterceptor::invoke()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 10
rs 10
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 Override;
12
use Ray\Aop\MethodInterceptor;
13
use Ray\Aop\MethodInvocation;
14
15
use function array_shift;
16
use function assert;
17
use function is_array;
18
use function is_string;
19
use function uri_template;
20
21
/** @psalm-import-type Query from Types */
22
final class EmbedInterceptor implements MethodInterceptor
23
{
24
    private const SELF_LINK = '_self';
25
26
    private readonly ResourceInterface $resource;
27
28
    public function __construct(
29
        ResourceInterface $resource,
30
    ) {
31
        $this->resource = clone $resource;
0 ignored issues
show
Bug introduced by
The property resource is declared read-only in BEAR\Resource\EmbedInterceptor.
Loading history...
32
    }
33
34
    /**
35
     * {@inheritDoc}
36
     *
37
     * @throws EmbedException
38
     */
39
    #[Override]
40
    public function invoke(MethodInvocation $invocation)
41
    {
42
        $ro = $invocation->getThis();
43
        assert($ro instanceof ResourceObject);
44
        $query = $this->getArgsByInvocation($invocation);
45
        $embeds = $invocation->getMethod()->getAnnotations();
46
        $this->embedResource($embeds, $ro, $query);
47
48
        return $invocation->proceed();
49
    }
50
51
    /**
52
     * @param array<Embed|object> $embeds
53
     * @param Query               $query
0 ignored issues
show
Bug introduced by
The type BEAR\Resource\Query was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
54
     *
55
     * @throws EmbedException
56
     *
57
     * @psalm-suppress NoInterfaceProperties
58
     * @psalm-suppress MixedMethodCall
59
     */
60
    private function embedResource(array $embeds, ResourceObject $ro, array $query): void
61
    {
62
        foreach ($embeds as $embed) {
63
            if (! $embed instanceof Embed) {
64
                continue;
65
            }
66
67
            try {
68
                $templateUri = $this->getFullUri($embed->src, $ro);
69
                $uri = uri_template($templateUri, $query);
70
                /** @var Request $request */ // phpcs:ignore SlevomatCodingStandard.PHP.RequireExplicitAssertion.RequiredExplicitAssertion
71
                $request = $this->resource->get->uri($uri); // @phpstan-ignore-line
72
                $this->prepareBody($ro, $embed);
73
74
                if ($embed->rel === self::SELF_LINK) {
75
                    $this->linkSelf($request, $ro);
76
77
                    continue;
78
                }
79
80
                assert(is_array($ro->body));
81
82
                $ro->body[$embed->rel] = clone $request;
83
            } catch (BadRequestException $e) {
84
                // wrap ResourceNotFound or Uri exception
85
                throw new EmbedException($embed->src, 500, $e);
86
            }
87
        }
88
    }
89
90
    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
    public function prepareBody(ResourceObject $ro, Embed $embed): void
100
    {
101
        if ($ro->body === null) {
102
            $ro->body = [];
103
        }
104
105
        if (! is_array($ro->body)) {
106
            throw new LinkException($embed->rel); // @codeCoverageIgnore
107
        }
108
    }
109
110
    /**
111
     * @param MethodInvocation<object> $invocation
112
     *
113
     * @return array<string, mixed>
114
     */
115
    private function getArgsByInvocation(MethodInvocation $invocation): array
116
    {
117
        /** @var list<scalar> $args */
118
        $args = $invocation->getArguments()->getArrayCopy();
119
        $params = $invocation->getMethod()->getParameters();
120
        $namedParameters = [];
121
        foreach ($params as $param) {
122
            $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

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