Completed
Push — master ( b1054d...eba5f2 )
by Christian
07:28 queued 11s
created

FormatNegotiator::sanitize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
/*
4
 * This file is part of the FOSRestBundle package.
5
 *
6
 * (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FOS\RestBundle\Negotiation;
13
14
use FOS\RestBundle\Util\StopFormatListenerException;
15
use Negotiation\Accept;
16
use Negotiation\Negotiator as BaseNegotiator;
17
use Symfony\Component\HttpFoundation\Request;
18
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
19
use Symfony\Component\HttpFoundation\RequestStack;
20
21
/**
22
 * @author Ener-Getick <[email protected]>
23
 *
24
 * @final since 2.8
25
 */
26
class FormatNegotiator extends BaseNegotiator
27
{
28
    private $map = [];
29
    private $requestStack;
30
    private $mimeTypes;
31
32 22
    public function __construct(RequestStack $requestStack, array $mimeTypes = array())
33
    {
34 22
        $this->requestStack = $requestStack;
35 22
        $this->mimeTypes = $mimeTypes;
36 22
    }
37
38
    /**
39
     * @param RequestMatcherInterface $requestMatcher
40
     * @param array                   $options
41
     */
42 21
    public function add(RequestMatcherInterface $requestMatcher, array $options = [])
43
    {
44 21
        $this->map[] = [$requestMatcher, $options];
45 21
    }
46
47
    /**
48
     * {@inheritdoc}
49
     * The best format is also determined in function of the bundle configuration.
50
     *
51
     * @throws StopFormatListenerException
52
     */
53 19
    public function getBest($header, array $priorities = [])
54
    {
55 19
        $request = $this->getRequest();
56 19
        $header = $header ?: $request->headers->get('Accept');
57
58 19
        foreach ($this->map as $elements) {
59
            // Check if the current RequestMatcherInterface matches the current request
60 18
            if (!$elements[0]->matches($request)) {
61 1
                continue;
62
            }
63 18
            $options = &$elements[1]; // Do not reallow memory for this variable
64
65 18
            if (!empty($options['stop'])) {
66 1
                throw new StopFormatListenerException('Stopped format listener');
67
            }
68 17
            if (empty($options['priorities']) && empty($priorities)) {
69 2
                if (!empty($options['fallback_format'])) {
70 2
                    return new Accept($request->getMimeType($options['fallback_format']));
71
                }
72
73 1
                continue;
74
            }
75
76 15
            if (isset($options['prefer_extension']) && $options['prefer_extension'] && !isset($extensionHeader)) {
77 10
                $extension = pathinfo($request->getPathInfo(), PATHINFO_EXTENSION);
78
79 10
                if (!empty($extension)) {
80
                    // $extensionHeader will now be either a non empty string or an empty string
81 5
                    $extensionHeader = $request->getMimeType($extension);
82
83 5
                    if ($extensionHeader) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $extensionHeader 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...
84 2
                        $header = $extensionHeader.'; q='.$options['prefer_extension'].($header ? ','.$header : '');
85
                    }
86
                }
87
            }
88
89 15
            if ($header) {
90 14
                $mimeTypes = $this->normalizePriorities($request,
91 14
                    empty($priorities) ? $options['priorities'] : $priorities
92
                );
93
94 14
                $mimeType = parent::getBest($header, $mimeTypes);
95
96 14
                if (null !== $mimeType) {
97 13
                    return $mimeType;
98
                }
99
            }
100
101 2
            if (isset($options['fallback_format'])) {
102
                // if false === fallback_format then we fail here instead of considering more rules
103 2
                if (false === $options['fallback_format']) {
104
                    return;
105
                }
106
107
                // stop looking at rules since we have a fallback defined
108 2
                return new Accept($request->getMimeType($options['fallback_format']));
109
            }
110
        }
111 3
    }
112
113
    /**
114
     * @param array $values
115
     *
116
     * @return array
117
     */
118 14
    private function sanitize(array $values)
119
    {
120
        return array_map(function ($value) {
121 14
            return preg_replace('/\s+/', '', strtolower($value));
122 14
        }, $values);
123
    }
124
125
    /**
126
     * Transform the format (json, html, ...) to their mimeType form (application/json, text/html, ...).
127
     *
128
     * @param Request  $request
129
     * @param string[] $priorities
130
     *
131
     * @return string[] formatted priorities
132
     */
133 14
    private function normalizePriorities(Request $request, array $priorities)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
134
    {
135 14
        $priorities = $this->sanitize($priorities);
136
137 14
        $mimeTypes = array();
138 14
        foreach ($priorities as $priority) {
139 14
            if (strpos($priority, '/')) {
140 4
                $mimeTypes[] = $priority;
141
142 4
                continue;
143
            }
144
145 11
            $mimeTypes = array_merge($mimeTypes, Request::getMimeTypes($priority));
146
147 11
            if (isset($this->mimeTypes[$priority])) {
148 3
                foreach ($this->mimeTypes[$priority] as $mimeType) {
149 3
                    $mimeTypes[] = $mimeType;
150
                }
151
            }
152
        }
153
154 14
        return $mimeTypes;
155
    }
156
157
    /**
158
     * @throws \RuntimeException
159
     *
160
     * @return Request
161
     */
162 19 View Code Duplication
    private function getRequest()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
163
    {
164 19
        $request = $this->requestStack->getCurrentRequest();
165 19
        if (null === $request) {
166
            throw new \RuntimeException('There is no current request.');
167
        }
168
169 19
        return $request;
170
    }
171
}
172