Completed
Push — master ( ca1131...42ef77 )
by Woody
03:33
created

FormattedResponder::sortValues()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
ccs 3
cts 3
cp 1
nc 1
cc 1
eloc 2
nop 0
crap 1
1
<?php
2
3
namespace Equip\Responder;
4
5
use Negotiation\Negotiator;
6
use InvalidArgumentException;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use Equip\Adr\PayloadInterface;
10
use Equip\Adr\ResponderInterface;
11
use Equip\Compatibility\StructureWithDataAlias;
12
use Equip\Formatter\AbstractFormatter;
13
use Equip\Formatter\JsonFormatter;
14
use Equip\Resolver\ResolverTrait;
15
use Equip\Structure\SortedDictionary;
16
use Relay\ResolverInterface;
17
18
class FormattedResponder extends SortedDictionary implements ResponderInterface
19
{
20
    use ResolverTrait;
21
    use StructureWithDataAlias;
22
23
    /**
24
     * @var Negotiator
25
     */
26
    private $negotiator;
27
28
    /**
29
     * @param Negotiator $negotiator
30
     * @param ResolverInterface $resolver
31
     */
32 10
    public function __construct(
33
        Negotiator $negotiator,
34
        ResolverInterface $resolver,
35
        array $formatters = [
36
            JsonFormatter::class => 1.0,
37
        ]
38
    ) {
39 10
        $this->negotiator = $negotiator;
40 10
        $this->resolver   = $resolver;
41
42 10
        parent::__construct($formatters);
43 10
    }
44
45
    /**
46
     * @inheritDoc
47
     */
48 5
    public function __invoke(
49
        ServerRequestInterface $request,
50
        ResponseInterface $response,
51
        PayloadInterface $payload
52
    ) {
53 5
        if ($this->hasOutput($payload)) {
54 2
            $formatter = $this->formatter($request);
55 2
            $response = $this->format($response, $formatter, $payload);
56 2
        }
57
58 5
        return $response;
59
    }
60
61
    /**
62
     * Determine if the payload has usable output
63
     *
64
     * @param PayloadInterface $payload
65
     *
66
     * @return boolean
67
     */
68 5
    protected function hasOutput(PayloadInterface $payload)
69
    {
70 5
        return (bool) $payload->getOutput();
71
    }
72
73
    /**
74
     * Retrieve a map of accepted priorities with the responsible formatter.
75
     *
76
     * @return array
77
     */
78 2
    protected function priorities()
79
    {
80 2
        $priorities = [];
81
82 2
        foreach ($this as $formatter => $quality) {
83 2
            foreach ($formatter::accepts() as $type) {
84 2
                $priorities[$type] = $formatter;
85 2
            }
86 2
        }
87
88 2
        return $priorities;
89
    }
90
91
    /**
92
     * Retrieve the formatter to use for the current request.
93
     *
94
     * Uses content negotiation to find the best available output format for
95
     * the requested content type.
96
     *
97
     * @param ServerRequestInterface $request
98
     *
99
     * @return AbstractFormatter
100
     */
101 2
    protected function formatter(ServerRequestInterface $request)
102
    {
103 2
        $accept = $request->getHeaderLine('Accept');
104 2
        $priorities = $this->priorities();
105
106 2
        if (!empty($accept)) {
107 1
            $preferred = $this->negotiator->getBest($accept, array_keys($priorities));
108 1
        }
109
110 2
        if (!empty($preferred)) {
111 1
            $formatter = $priorities[$preferred->getValue()];
112 1
        } else {
113 1
            $formatter = array_shift($priorities);
114
        }
115
116 2
        return $this->resolve($formatter);
117
    }
118
119
    /**
120
     * Update the response by formatting the payload.
121
     *
122
     * @param ResponseInterface $response
123
     * @param AbstractFormatter $formatter
124
     * @param PayloadInterface $payload
125
     *
126
     * @return ResponseInterface
127
     */
128 2
    protected function format(
129
        ResponseInterface $response,
130
        AbstractFormatter $formatter,
131
        PayloadInterface  $payload
132
    ) {
133 2
        $response = $response->withStatus($formatter->status($payload));
134 2
        $response = $response->withHeader('Content-Type', $formatter->type());
1 ignored issue
show
Bug introduced by
The method type() cannot be called from this context as it is declared protected in class Equip\Formatter\AbstractFormatter.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
135
136
        // Overwrite the body instead of making a copy and dealing with the stream.
137 2
        $response->getBody()->write($formatter->body($payload));
1 ignored issue
show
Bug introduced by
The method body() cannot be called from this context as it is declared protected in class Equip\Formatter\AbstractFormatter.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
138
139 2
        return $response;
140
    }
141
142
    /**
143
     * @inheritDoc
144
     *
145
     * @throws InvalidArgumentException
146
     *  If a formatter does not implement the correct interface, or does not
147
     *  have a quality value.
148
     */
149 10
    protected function assertValid(array $data)
150
    {
151 10
        parent::assertValid($data);
152
153 10
        foreach ($data as $formatter => $quality) {
154 10
            if (!is_subclass_of($formatter, AbstractFormatter::class)) {
155 1
                throw new InvalidArgumentException(sprintf(
156 1
                    'All formatters in `%s` must implement `%s`',
157 1
                    static::class,
158
                    AbstractFormatter::class
159 1
                ));
160
            }
161
162 10
            if (!is_float($quality)) {
163 1
                throw new InvalidArgumentException(sprintf(
164 1
                    'All formatters in `%s` must have a quality value',
165 1
                    static::class
166 1
                ));
167
            }
168 10
        }
169 10
    }
170
171
    /**
172
     * @inheritDoc
173
     */
174 3
    protected function sortValues()
175
    {
176 3
        arsort($this->values);
177 3
    }
178
}
179