Completed
Pull Request — master (#1)
by Woody
27:07 queued 01:07
created

FormattedResponder::priorities()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4286
cc 3
eloc 6
nc 3
nop 0
crap 3
1
<?php
2
3
namespace Equip\Responder;
4
5
use Destrukt\Dictionary;
6
use Negotiation\Negotiator;
7
use InvalidArgumentException;
8
use Psr\Http\Message\ServerRequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Equip\Adr\PayloadInterface;
11
use Equip\Adr\ResponderInterface;
12
use Equip\Formatter\AbstractFormatter;
13
use Equip\Formatter\JsonFormatter;
14
use Equip\Resolver\ResolverTrait;
15
use Relay\ResolverInterface;
16
17
class FormattedResponder extends Dictionary implements ResponderInterface
18
{
19
    use ResolverTrait;
20
21
    /**
22
     * @var Negotiator
23
     */
24
    private $negotiator;
25
26
    /**
27
     * @param Negotiator $negotiator
28
     * @param ResolverInterface $resolver
29
     */
30 9
    public function __construct(
31
        Negotiator $negotiator,
32
        ResolverInterface $resolver,
33
        array $formatters = [
34
            JsonFormatter::class => 1.0,
35
        ]
36
    ) {
37 9
        $this->negotiator = $negotiator;
38 9
        $this->resolver   = $resolver;
39
40 9
        parent::__construct($formatters);
41 9
    }
42
43
    /**
44
     * @inheritDoc
45
     */
46 9
    public function validate(array $data)
47
    {
48 9
        parent::validate($data);
49
50 9
        foreach ($data as $formatter => $quality) {
51 9
            if (!is_subclass_of($formatter, AbstractFormatter::class)) {
52 1
                throw new InvalidArgumentException(sprintf(
53 1
                    'All formatters in `%s` must implement `%s`',
54 1
                    static::class,
55 1
                    AbstractFormatter::class
56
                ));
57
            }
58
59 9
            if (!is_float($quality)) {
60 1
                throw new InvalidArgumentException(sprintf(
61 1
                    'All formatters in `%s` must have a quality value',
62 9
                    static::class
63
                ));
64
            }
65
        }
66 9
    }
67
68
    /**
69
     * @inheritDoc
70
     */
71 5
    public function __invoke(
72
        ServerRequestInterface $request,
73
        ResponseInterface $response,
74
        PayloadInterface $payload
75
    ) {
76 5
        if ($this->hasOutput($payload)) {
77 2
            $formatter = $this->formatter($request);
78 2
            $response = $this->format($response, $formatter, $payload);
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 2 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
79
        }
80
81 5
        return $response;
82
    }
83
84
    /**
85
     * Determine if the payload has usable output
86
     *
87
     * @param PayloadInterface $payload
88
     *
89
     * @return boolean
90
     */
91 5
    protected function hasOutput(PayloadInterface $payload)
92
    {
93 5
        return (bool) $payload->getOutput();
94
    }
95
96
    /**
97
     * Retrieve a map of accepted priorities with the responsible formatter.
98
     *
99
     * @return array
100
     */
101 2
    protected function priorities()
102
    {
103 2
        $priorities = [];
104
105 2
        foreach ($this as $formatter => $quality) {
106 2
            foreach ($formatter::accepts() as $type) {
107 2
                $priorities[$type] = $formatter;
108
            }
109
        }
110
111 2
        return $priorities;
112
    }
113
114
    /**
115
     * Retrieve the formatter to use for the current request.
116
     *
117
     * Uses content negotiation to find the best available output format for
118
     * the requested content type.
119
     *
120
     * @param ServerRequestInterface $request
121
     *
122
     * @return AbstractFormatter
123
     */
124 2
    protected function formatter(ServerRequestInterface $request)
125
    {
126 2
        $accept = $request->getHeaderLine('Accept');
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned with surrounding assignments; expected 5 spaces but found 1 space

This check looks for multiple assignments in successive lines of code. It will report an issue if the operators are not in a straight line.

To visualize

$a = "a";
$ab = "ab";
$abc = "abc";

will produce issues in the first and second line, while this second example

$a   = "a";
$ab  = "ab";
$abc = "abc";

will produce no issues.

Loading history...
127 2
        $priorities = $this->priorities();
128
129 2
        if (!empty($accept)) {
130 1
            $preferred = $this->negotiator->getBest($accept, array_keys($priorities));
131
        }
132
133 2
        if (!empty($preferred)) {
134 1
            $formatter = $priorities[$preferred->getValue()];
135
        } else {
136 1
            $formatter = array_shift($priorities);
137
        }
138
139 2
        return $this->resolve($formatter);
140
    }
141
142
    /**
143
     * Update the response by formatting the payload.
144
     *
145
     * @param ResponseInterface $response
146
     * @param AbstractFormatter $formatter
147
     * @param PayloadInterface $payload
148
     *
149
     * @return ResponseInterface
150
     */
151 2
    protected function format(
152
        ResponseInterface $response,
153
        AbstractFormatter $formatter,
154
        PayloadInterface  $payload
155
    ) {
156 2
        $response = $response->withStatus($formatter->status($payload));
157 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...
158
159
        // Overwrite the body instead of making a copy and dealing with the stream.
160 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...
161
162 2
        return $response;
163
    }
164
}
165