Issues (44)

src/EmbedInterceptor.php (2 issues)

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
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
                if ($ro->body === null) {
70 7
                    $ro->body = [];
71
                }
72 9
73
                if (! is_array($ro->body)) {
74 9
                    throw new LinkException($embed->rel); // @codeCoverageIgnore
75 1
                }
76
77
                if ($embed->rel === self::SELF_LINK) {
78 9
                    $this->linkSelf($request, $ro);
79
80
                    continue;
81 9
                }
82
83 9
                $ro->body[$embed->rel] = clone $request;
84 9
            } catch (BadRequestException $e) {
85 9
                // wrap ResourceNotFound or Uri exception
86 9
                throw new EmbedException($embed->src, 500, $e);
87 9
            }
88
        }
89
    }
90 9
91
    private function getFullUri(string $uri, ResourceObject $ro): string
92
    {
93
        if ($uri[0] === '/') {
94
            $uri = "{$ro->uri->scheme}://{$ro->uri->host}" . $uri;
95
        }
96
97
        return $uri;
98
    }
99
100
    /** @return array<string, mixed> */
101
    private function getArgsByInvocation(MethodInvocation $invocation): array
102
    {
103
        /** @var list<scalar> $args */
104
        $args = $invocation->getArguments()->getArrayCopy();
105
        $params = $invocation->getMethod()->getParameters();
106
        $namedParameters = [];
107
        foreach ($params as $param) {
108
            $namedParameters[$param->name] = array_shift($args);
0 ignored issues
show
$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

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