Completed
Push — hal ( a251eb )
by Akihito
02:11
created

HalRenderer::getReverseMatchedLink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BEAR\Resource;
6
7
use BEAR\Resource\Annotation\Link;
8
use Doctrine\Common\Annotations\Reader;
9
use function is_iterable;
10
use function is_scalar;
11
use Nocarrier\Hal;
12
13
class HalRenderer implements RenderInterface
14
{
15
    /**
16
     * @var Reader
17
     */
18
    private $reader;
19
20
    /**
21
     * @var HalLink
22
     */
23
    private $link;
24
25
    public function __construct(Reader $reader, HalLink $link)
26
    {
27
        $this->reader = $reader;
28
        $this->link = $link;
29
    }
30
31
    /**
32
     * {@inheritdoc}
33
     */
34
    public function render(ResourceObject $ro)
35
    {
36
        $this->renderHal($ro);
37
        $this->updateHeaders($ro);
38
39
        return (string) $ro->view;
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     *
45
     * @throws \RuntimeException
46
     */
47
    public function renderHal(ResourceObject $ro)
48
    {
49
        [$ro, $body] = $this->valuate($ro);
0 ignored issues
show
Bug introduced by
The variable $body does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
50
        $method = 'on' . ucfirst($ro->uri->method);
51
        $hasMethod = method_exists($ro, $method);
52
        $annotations = $hasMethod ? $this->reader->getMethodAnnotations(new \ReflectionMethod($ro, $method)) : [];
53
        /* @var $annotations Link[] */
54
        $hal = $this->getHal($ro->uri, (array) $body, $annotations);
55
        $ro->view = $hal->asJson(true) . PHP_EOL;
56
        $ro->headers['Content-Type'] = 'application/hal+json';
57
    }
58
59
    private function valuateElements(ResourceObject $ro) : void
60
    {
61
        if (! is_iterable($ro->body)) {
62
            return;
63
        }
64
        foreach ($ro->body as $key => &$embeded) {
65
            if ($embeded instanceof AbstractRequest) {
66
                // @codeCoverageIgnoreStart
67
                if ($this->isDifferentSchema($ro, $embeded->resourceObject)) {
68
                    $ro->body['_embedded'][$key] = $embeded()->body;
69
                    unset($ro->body[$key]);
70
71
                    continue;
72
                }
73
                // @codeCoverageIgnoreEnd
74
                unset($ro->body[$key]);
75
                $view = $this->render($embeded());
76
                $ro->body['_embedded'][$key] = json_decode($view);
77
            }
78
        }
79
    }
80
81
    /**
82
     * @codeCoverageIgnore
83
     */
84
    private function isDifferentSchema(ResourceObject $parentRo, ResourceObject $childRo) : bool
85
    {
86
        return $parentRo->uri->scheme . $parentRo->uri->host !== $childRo->uri->scheme . $childRo->uri->host;
87
    }
88
89
    private function getHal(AbstractUri $uri, array $body, array $annotations) : Hal
90
    {
91
        $query = $uri->query ? '?' . http_build_query($uri->query) : '';
92
        $path = $uri->path . $query;
93
        $selfLink = $this->link->getReverseLink($path);
94
        $hal = new Hal($selfLink, $body);
95
96
        return $this->link->addHalLink($body, $annotations, $hal);
97
    }
98
99
    private function valuate(ResourceObject $ro) : array
100
    {
101
        if (is_scalar($ro->body) && $ro->body !== null) {
102
            $ro->body = ['value' => $ro->body];
103
        }
104
        // evaluate all request in body.
105
        $this->valuateElements($ro);
106
107
        return [$ro, $ro->body];
108
    }
109
110
    private function updateHeaders(ResourceObject $ro) : void
111
    {
112
        $ro->headers['content-type'] = 'application/hal+json';
113
        if (isset($ro->headers['Location'])) {
114
            $ro->headers['Location'] = $this->link->getReverseLink($ro->headers['Location']);
115
        }
116
    }
117
}
118