Completed
Pull Request — master (#81)
by
unknown
02:53 queued 52s
created

FormatNegotiator   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 195
Duplicated Lines 5.13 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
wmc 21
lcom 2
cbo 6
dl 10
loc 195
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getFormat() 0 4 1
A defaultFormat() 0 6 1
A addFormat() 0 6 2
A __construct() 0 6 2
A __invoke() 0 16 4
A getFromExtension() 5 14 4
B getFromHeader() 5 20 7

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 array Available formats with the mime types
27
     */
28
    private $formats = [
29
        //text
30
        'html' => [['html', 'htm', 'php'], ['text/html', 'application/xhtml+xml']],
31
        'txt' => [['txt'], ['text/plain']],
32
        'css' => [['css'], ['text/css']],
33
        'json' => [['json'], ['application/json', 'text/json', 'application/x-json']],
34
        'jsonp' => [['jsonp'], ['text/javascript', 'application/javascript', 'application/x-javascript']],
35
        'js' => [['js'], ['text/javascript', 'application/javascript', 'application/x-javascript']],
36
37
        //xml
38
        'rdf' => [['rdf'], ['application/rdf+xml']],
39
        'rss' => [['rss'], ['application/rss+xml']],
40
        'atom' => [['atom'], ['application/atom+xml']],
41
        'xml' => [['xml'], ['text/xml', 'application/xml', 'application/x-xml']],
42
43
        //images
44
        'bmp' => [['bmp'], ['image/bmp']],
45
        'gif' => [['gif'], ['image/gif']],
46
        'png' => [['png'], ['image/png', 'image/x-png']],
47
        'jpg' => [['jpg', 'jpeg', 'jpe'], ['image/jpeg', 'image/jpg']],
48
        'svg' => [['svg', 'svgz'], ['image/svg+xml']],
49
        'psd' => [['psd'], ['image/vnd.adobe.photoshop']],
50
        'eps' => [['ai', 'eps', 'ps'], ['application/postscript']],
51
        'ico' => [['ico'], ['image/x-icon', 'image/vnd.microsoft.icon']],
52
53
        //audio/video
54
        'mov' => [['mov', 'qt'], ['video/quicktime']],
55
        'mp3' => [['mp3'], ['audio/mpeg']],
56
        'mp4' => [['mp4'], ['video/mp4']],
57
        'ogg' => [['ogg'], ['audio/ogg']],
58
        'ogv' => [['ogv'], ['video/ogg']],
59
        'webm' => [['webm'], ['video/webm']],
60
        'webp' => [['webp'], ['image/webp']],
61
62
        //fonts
63
        'eot' => [['eot'], ['application/vnd.ms-fontobject']],
64
        'otf' => [['otf'], ['font/opentype', 'application/x-font-opentype']],
65
        'ttf' => [['ttf'], ['font/ttf', 'application/font-ttf', 'application/x-font-ttf']],
66
        'woff' => [['woff'], ['font/woff', 'application/font-woff', 'application/x-font-woff']],
67
        'woff2' => [['woff2'], ['font/woff2', 'application/font-woff2', 'application/x-font-woff2']],
68
69
        //other formats
70
        'pdf' => [['pdf'], ['application/pdf', 'application/x-download']],
71
        'zip' => [['zip'], ['application/zip', 'application/x-zip', 'application/x-zip-compressed']],
72
        'rar' => [['rar'], ['application/rar', 'application/x-rar', 'application/x-rar-compressed']],
73
        'exe' => [['exe'], ['application/x-msdownload']],
74
        'msi' => [['msi'], ['application/x-msdownload']],
75
        'cab' => [['cab'], ['application/vnd.ms-cab-compressed']],
76
        'doc' => [['doc'], ['application/msword']],
77
        'rtf' => [['rtf'], ['application/rtf']],
78
        'xls' => [['xls'], ['application/vnd.ms-excel']],
79
        'ppt' => [['ppt'], ['application/vnd.ms-powerpoint']],
80
        'odt' => [['odt'], ['application/vnd.oasis.opendocument.text']],
81
        'ods' => [['ods'], ['application/vnd.oasis.opendocument.spreadsheet']],
82
    ];
83
84
    /**
85
     * Returns the format.
86
     *
87
     * @param ServerRequestInterface $request
88
     *
89
     * @return string|null
90
     */
91
    public static function getFormat(ServerRequestInterface $request)
92
    {
93
        return self::getAttribute($request, self::KEY);
94
    }
95
96
    /**
97
     * Add a new format.
98
     *
99
     * @param string     $format
100
     * @param array      $mimeTypes
101
     * @param array|null $extensions
102
     *
103
     * @return self
104
     */
105
    public function addFormat($format, array $mimeTypes, array $extensions = null)
106
    {
107
        $this->formats[$format] = [is_null($extensions) ? [$format] : $extensions, $mimeTypes];
108
109
        return $this;
110
    }
111
112
    /**
113
     * Set the default format.
114
     *
115
     * @param string $format
116
     *
117
     * @return self
118
     */
119
    public function defaultFormat($format)
120
    {
121
        $this->default = $format;
122
123
        return $this;
124
    }
125
126
    /**
127
     * @param array|null $formats Formats which the server supports, in priority order.
128
     */
129
    public function __construct($formats = null)
130
    {
131
        if (!empty($formats)) {
132
            $this->formats = $formats;
133
        }
134
    }
135
136
    /**
137
     * Execute the middleware.
138
     *
139
     * @param ServerRequestInterface $request
140
     * @param ResponseInterface      $response
141
     * @param callable               $next
142
     *
143
     * @return ResponseInterface
144
     */
145
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
146
    {
147
        $format = $this->getFromExtension($request) ?: $this->getFromHeader($request) ?: $this->default;
148
        $contentType = $this->formats[$format][1][0].'; charset=utf-8';
149
150
        $response = $next(
151
            self::setAttribute($request, self::KEY, $format),
152
            $response->withHeader('Content-Type', $contentType)
153
        );
154
155
        if (!$response->hasHeader('Content-Type')) {
156
            $response = $response->withHeader('Content-Type', $contentType);
157
        }
158
159
        return $response;
160
    }
161
162
    /**
163
     * Returns the format using the file extension.
164
     *
165
     * @return null|string
166
     */
167
    private function getFromExtension(ServerRequestInterface $request)
168
    {
169
        $extension = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
170
171
        if (empty($extension)) {
172
            return;
173
        }
174
175 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...
176
            if (in_array($extension, $data[0], true)) {
177
                return $format;
178
            }
179
        }
180
    }
181
182
    /**
183
     * Returns the format using the Accept header.
184
     *
185
     * @return null|string
186
     */
187
    private function getFromHeader(ServerRequestInterface $request)
188
    {
189
        $accept = $request->getHeaderLine('Accept');
190
191
        //If the client accepts everything, then return null and allow the default to be used.
192
        if (empty($accept) || $accept === "*" || $accept === "*/*") {
193
            return null;
194
        }
195
196
        $headers = call_user_func_array('array_merge', array_column($this->formats, 1));
197
        $mime = $this->negotiateHeader($accept, new Negotiator(), $headers);
198
199
        if ($mime !== null) {
200 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...
201
                if (in_array($mime, $data[1], true)) {
202
                    return $format;
203
                }
204
            }
205
        }
206
    }
207
}
208