Completed
Push — master ( 512ee3...1ba870 )
by Oscar
03:27
created

FormatNegotiator   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 13
Bugs 2 Features 3
Metric Value
wmc 13
c 13
b 2
f 3
lcom 2
cbo 6
dl 0
loc 131
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getFormat() 0 4 1
A addFormat() 0 6 1
A defaultFormat() 0 6 1
A __invoke() 0 11 4
A getFromExtension() 0 6 2
A getFromHeader() 0 12 4
1
<?php
2
3
namespace Psr7Middlewares\Middleware;
4
5
use Psr7Middlewares\Middleware;
6
use Psr7Middlewares\Utils;
7
use Psr\Http\Message\ServerRequestInterface;
8
use Psr\Http\Message\ResponseInterface;
9
use Negotiation\Negotiator;
10
11
/**
12
 * Middleware returns the client preferred format.
13
 */
14
class FormatNegotiator
15
{
16
    use Utils\NegotiateTrait;
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
        'html' => ['text/html', 'application/xhtml+xml'],
30
        'css' => ['text/css'],
31
        'gif' => ['image/gif'],
32
        'png' => ['image/png', 'image/x-png'],
33
        'jpg' => ['image/jpeg', 'image/jpg'],
34
        'jpeg' => ['image/jpeg', 'image/jpg'],
35
        'json' => ['application/json', 'text/json', 'application/x-json'],
36
        'jsonp' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
37
        'js' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
38
        'pdf' => ['application/pdf', 'application/x-download'],
39
        'rdf' => ['application/rdf+xml'],
40
        'rss' => ['application/rss+xml'],
41
        'atom' => ['application/atom+xml'],
42
        'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
43
        'txt' => ['text/plain'],
44
        'mp4' => ['video/mp4'],
45
        'ogg' => ['audio/ogg'],
46
        'ogv' => ['video/ogg'],
47
        'webm' => ['video/webm'],
48
        'webp' => ['image/webp'],
49
        'svg' => ['image/svg+xml'],
50
        'zip' => ['application/zip', 'application/x-zip', 'application/x-zip-compressed'],
51
    ];
52
53
    /**
54
     * Returns the format.
55
     *
56
     * @param ServerRequestInterface $request
57
     *
58
     * @return string|null
59
     */
60
    public static function getFormat(ServerRequestInterface $request)
61
    {
62
        return Middleware::getAttribute($request, self::KEY);
63
    }
64
65
    /**
66
     * Add a new format.
67
     *
68
     * @param string $format
69
     * @param array  $mimeTypes
70
     *
71
     * @return self
72
     */
73
    public function addFormat($format, array $mimeTypes)
74
    {
75
        $this->formats[$format] = $mimeTypes;
76
77
        return $this;
78
    }
79
80
    /**
81
     * Set the default format.
82
     *
83
     * @param string $format
84
     *
85
     * @return self
86
     */
87
    public function defaultFormat($format)
88
    {
89
        $this->default = $format;
90
91
        return $this;
92
    }
93
94
    /**
95
     * Execute the middleware.
96
     *
97
     * @param ServerRequestInterface $request
98
     * @param ResponseInterface      $response
99
     * @param callable               $next
100
     *
101
     * @return ResponseInterface
102
     */
103
    public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
104
    {
105
        $format = $this->getFromExtension($request) ?: $this->getFromHeader($request) ?: $this->default;
106
107
        if ($format) {
108
            $request = Middleware::setAttribute($request, self::KEY, $format);
109
            $response = $response->withHeader('Content-Type', $this->formats[$format][0].'; charset=utf-8');
110
        }
111
112
        return $next($request, $response);
113
    }
114
115
    /**
116
     * Returns the format using the file extension.
117
     *
118
     * @return null|string
119
     */
120
    private function getFromExtension(ServerRequestInterface $request)
121
    {
122
        $format = strtolower(pathinfo($request->getUri()->getPath(), PATHINFO_EXTENSION));
123
124
        return isset($this->formats[$format]) ? $format : null;
125
    }
126
127
    /**
128
     * Returns the format using the Accept header.
129
     *
130
     * @return null|string
131
     */
132
    private function getFromHeader(ServerRequestInterface $request)
133
    {
134
        $format = $this->negotiateHeader($request->getHeaderLine('Accept'), new Negotiator(), call_user_func_array('array_merge', array_values($this->formats)));
135
136
        if ($format) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $format of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
137
            foreach ($this->formats as $extension => $headers) {
138
                if (in_array($format, $headers)) {
139
                    return $extension;
140
                }
141
            }
142
        }
143
    }
144
}
145