Completed
Pull Request — master (#81)
by
unknown
01:52
created

FormatNegotiator::getFromHeader()   D

Complexity

Conditions 10
Paths 9

Size

Total Lines 31
Code Lines 17

Duplication

Lines 5
Ratio 16.13 %

Importance

Changes 0
Metric Value
dl 5
loc 31
rs 4.8196
c 0
b 0
f 0
cc 10
eloc 17
nc 9
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Utils;
6
use Psr\Http\Message\ServerRequestInterface;
7
use Psr\Http\Message\ResponseInterface;
8
use Negotiation\Negotiator;
9
10
/**
11
 * Middleware returns the client preferred format.
12
 */
13
class FormatNegotiator
14
{
15
    use Utils\NegotiateTrait;
16
    use Utils\AttributeTrait;
17
18
    const KEY = 'FORMAT';
19
20
    /**
21
     * @var string Default format
22
     */
23
    private $default = 'html';
24
25
    /**
26
     * @var string[] Formats which the server supports
27
     */
28
    private $priorities = array();
29
30
    /**
31
     * @var array Available formats with the mime types
32
     */
33
    private $formats = [
34
        //text
35
        'html' => [['html', 'htm', 'php'], ['text/html', 'application/xhtml+xml']],
36
        'txt' => [['txt'], ['text/plain']],
37
        'css' => [['css'], ['text/css']],
38
        'json' => [['json'], ['application/json', 'text/json', 'application/x-json']],
39
        'jsonp' => [['jsonp'], ['text/javascript', 'application/javascript', 'application/x-javascript']],
40
        'js' => [['js'], ['text/javascript', 'application/javascript', 'application/x-javascript']],
41
42
        //xml
43
        'rdf' => [['rdf'], ['application/rdf+xml']],
44
        'rss' => [['rss'], ['application/rss+xml']],
45
        'atom' => [['atom'], ['application/atom+xml']],
46
        'xml' => [['xml'], ['text/xml', 'application/xml', 'application/x-xml']],
47
48
        //images
49
        'bmp' => [['bmp'], ['image/bmp']],
50
        'gif' => [['gif'], ['image/gif']],
51
        'png' => [['png'], ['image/png', 'image/x-png']],
52
        'jpg' => [['jpg', 'jpeg', 'jpe'], ['image/jpeg', 'image/jpg']],
53
        'svg' => [['svg', 'svgz'], ['image/svg+xml']],
54
        'psd' => [['psd'], ['image/vnd.adobe.photoshop']],
55
        'eps' => [['ai', 'eps', 'ps'], ['application/postscript']],
56
        'ico' => [['ico'], ['image/x-icon', 'image/vnd.microsoft.icon']],
57
58
        //audio/video
59
        'mov' => [['mov', 'qt'], ['video/quicktime']],
60
        'mp3' => [['mp3'], ['audio/mpeg']],
61
        'mp4' => [['mp4'], ['video/mp4']],
62
        'ogg' => [['ogg'], ['audio/ogg']],
63
        'ogv' => [['ogv'], ['video/ogg']],
64
        'webm' => [['webm'], ['video/webm']],
65
        'webp' => [['webp'], ['image/webp']],
66
67
        //fonts
68
        'eot' => [['eot'], ['application/vnd.ms-fontobject']],
69
        'otf' => [['otf'], ['font/opentype', 'application/x-font-opentype']],
70
        'ttf' => [['ttf'], ['font/ttf', 'application/font-ttf', 'application/x-font-ttf']],
71
        'woff' => [['woff'], ['font/woff', 'application/font-woff', 'application/x-font-woff']],
72
        'woff2' => [['woff2'], ['font/woff2', 'application/font-woff2', 'application/x-font-woff2']],
73
74
        //other formats
75
        'pdf' => [['pdf'], ['application/pdf', 'application/x-download']],
76
        'zip' => [['zip'], ['application/zip', 'application/x-zip', 'application/x-zip-compressed']],
77
        'rar' => [['rar'], ['application/rar', 'application/x-rar', 'application/x-rar-compressed']],
78
        'exe' => [['exe'], ['application/x-msdownload']],
79
        'msi' => [['msi'], ['application/x-msdownload']],
80
        'cab' => [['cab'], ['application/vnd.ms-cab-compressed']],
81
        'doc' => [['doc'], ['application/msword']],
82
        'rtf' => [['rtf'], ['application/rtf']],
83
        'xls' => [['xls'], ['application/vnd.ms-excel']],
84
        'ppt' => [['ppt'], ['application/vnd.ms-powerpoint']],
85
        'odt' => [['odt'], ['application/vnd.oasis.opendocument.text']],
86
        'ods' => [['ods'], ['application/vnd.oasis.opendocument.spreadsheet']],
87
    ];
88
89
    /**
90
     * Returns the format.
91
     *
92
     * @param ServerRequestInterface $request
93
     *
94
     * @return string|null
95
     */
96
    public static function getFormat(ServerRequestInterface $request)
97
    {
98
        return self::getAttribute($request, self::KEY);
99
    }
100
101
    /**
102
     * Add a new format.
103
     *
104
     * @param string     $format
105
     * @param array      $mimeTypes
106
     * @param array|null $extensions
107
     *
108
     * @return self
109
     */
110
    public function addFormat($format, array $mimeTypes, array $extensions = null)
111
    {
112
        $this->formats[$format] = [is_null($extensions) ? [$format] : $extensions, $mimeTypes];
113
114
        return $this;
115
    }
116
117
    /**
118
     * Set the default format.
119
     *
120
     * @param string $format
121
     *
122
     * @return self
123
     */
124
    public function defaultFormat($format)
125
    {
126
        $this->default = $format;
127
128
        return $this;
129
    }
130
131
    /**
132
     * Sets the formats which the server supports.
133
     *
134
     * @param string[] $priorities
135
     *
136
     * @return self
137
     */
138
    public function setPriorities($priorities)
139
    {
140
        $this->priorities = $priorities;
141
142
        return $this;
143
    }
144
145
    /**
146
     * Execute the middleware.
147
     *
148
     * @param ServerRequestInterface $request
149
     * @param ResponseInterface      $response
150
     * @param callable               $next
151
     *
152
     * @return ResponseInterface
153
     */
154
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
155
    {
156
        $format = $this->getFromExtension($request) ?: $this->getFromHeader($request) ?: $this->default;
157
        $contentType = $this->formats[$format][1][0].'; charset=utf-8';
158
159
        $response = $next(
160
            self::setAttribute($request, self::KEY, $format),
161
            $response->withHeader('Content-Type', $contentType)
162
        );
163
164
        if (!$response->hasHeader('Content-Type')) {
165
            $response = $response->withHeader('Content-Type', $contentType);
166
        }
167
168
        return $response;
169
    }
170
171
    /**
172
     * Returns the format using the file extension.
173
     *
174
     * @return null|string
175
     */
176
    private function getFromExtension(ServerRequestInterface $request)
177
    {
178
        $extension = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
179
180
        if (empty($extension)) {
181
            return;
182
        }
183
184 View Code Duplication
        foreach ($this->formats as $format => $data) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
            if (in_array($extension, $data[0], true)) {
186
                return $format;
187
            }
188
        }
189
    }
190
191
    /**
192
     * Returns the format using the Accept header.
193
     *
194
     * @return null|string
195
     */
196
    private function getFromHeader(ServerRequestInterface $request)
197
    {
198
        $accept = $request->getHeaderLine('Accept');
199
200
        //If the client accepts everything, then return null and allow the default to be used.
201
        if (empty($accept) || $accept === "*" || $accept === "*/*") {
202
            return null;
203
        }
204
205
        if (empty($this->priorities)) {
206
            $formats = $this->formats;
207
        } else {
208
            //Filter the list of formats.
209
            $formats = [];
210
            foreach ($this->priorities as $priority) {
211
                if (isset($this->formats[$priority])) {
212
                    $formats[$priority] = $this->formats[$priority];
213
                }
214
            }
215
        }
216
        $headers = call_user_func_array('array_merge', array_column($formats, 1));
217
        $mime = $this->negotiateHeader($accept, new Negotiator(), $headers);
218
219
        if ($mime !== null) {
220 View Code Duplication
            foreach ($this->formats as $format => $data) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
                if (in_array($mime, $data[1], true)) {
222
                    return $format;
223
                }
224
            }
225
        }
226
    }
227
}
228