Completed
Pull Request — 2.x (#2280)
by
unknown
09:25 queued 07:49
created

BaseFormatNegotiator::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
class BaseFormatNegotiator extends BaseNegotiator
22
{
23
    private $map = [];
24
    private $requestStack;
25
    private $mimeTypes;
26
27 29
    public function __construct(RequestStack $requestStack, array $mimeTypes = [])
28
    {
29 29
        $this->requestStack = $requestStack;
30 29
        $this->mimeTypes = $mimeTypes;
31 29
    }
32
33 27
    public function add(RequestMatcherInterface $requestMatcher, array $options = [])
34
    {
35 27
        $this->map[] = [$requestMatcher, $options];
36 27
    }
37
38
    /**
39
     * @internal
40
     */
41 24
    protected function doGetBest($header, array $priorities = [])
42
    {
43 24
        $request = $this->getRequest();
44 24
        $header = $header ?: $request->headers->get('Accept');
45
46 24
        foreach ($this->map as $elements) {
47
            // Check if the current RequestMatcherInterface matches the current request
48 22
            if (!$elements[0]->matches($request)) {
49 1
                continue;
50
            }
51 22
            $options = &$elements[1]; // Do not reallow memory for this variable
52
53 22
            if (!empty($options['stop'])) {
54 1
                throw new StopFormatListenerException('Stopped format listener');
55
            }
56 21
            if (empty($options['priorities']) && empty($priorities)) {
57 4
                if (!empty($options['fallback_format'])) {
58 4
                    return new Accept($request->getMimeType($options['fallback_format']));
59
                }
60
61 1
                continue;
62
            }
63
64 17
            if (isset($options['prefer_extension']) && $options['prefer_extension'] && !isset($extensionHeader)) {
65 12
                $extension = pathinfo($request->getPathInfo(), PATHINFO_EXTENSION);
66
67 12
                if (!empty($extension)) {
68
                    // $extensionHeader will now be either a non empty string or an empty string
69 6
                    $extensionHeader = $request->getMimeType($extension);
70
71 6
                    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...
72 3
                        $header = $extensionHeader.'; q='.$options['prefer_extension'].($header ? ','.$header : '');
73
                    }
74
                }
75
            }
76
77 17
            if ($header) {
78 16
                $mimeTypes = $this->normalizePriorities(
79 16
                    $request,
80 16
                    empty($priorities) ? $options['priorities'] : $priorities
81
                );
82
83 16
                $mimeType = parent::getBest($header, $mimeTypes);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getBest() instead of doGetBest()). Are you sure this is correct? If so, you might want to change this to $this->getBest().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
84
85 16
                if (null !== $mimeType) {
86 15
                    return $mimeType;
87
                }
88
            }
89
90 2
            if (isset($options['fallback_format'])) {
91
                // if false === fallback_format then we fail here instead of considering more rules
92 2
                if (false === $options['fallback_format']) {
93
                    return;
94
                }
95
96
                // stop looking at rules since we have a fallback defined
97 2
                return new Accept($request->getMimeType($options['fallback_format']));
98
            }
99
        }
100 4
    }
101
102 16
    private function sanitize(array $values): array
103
    {
104
        return array_map(function ($value) {
105 16
            return preg_replace('/\s+/', '', strtolower($value));
106 16
        }, $values);
107
    }
108
109
    /**
110
     * @param string[] $priorities
111
     *
112
     * @return string[] formatted priorities
113
     */
114 16
    private function normalizePriorities(Request $request, array $priorities): array
115
    {
116 16
        $priorities = $this->sanitize($priorities);
117
118 16
        $mimeTypes = [];
119 16
        foreach ($priorities as $priority) {
120 16
            if (strpos($priority, '/')) {
121 4
                $mimeTypes[] = $priority;
122
123 4
                continue;
124
            }
125
126 13
            if (method_exists(Request::class, 'getMimeTypes')) {
127 13
                $mimeTypes = array_merge($mimeTypes, Request::getMimeTypes($priority));
128
            } elseif (null !== $request->getMimeType($priority)) {
129
                $class = new \ReflectionClass(Request::class);
130
                $properties = $class->getStaticProperties();
131
                $mimeTypes = array_merge($mimeTypes, $properties['formats'][$priority]);
132
            }
133
134 13
            if (isset($this->mimeTypes[$priority])) {
135 3
                foreach ($this->mimeTypes[$priority] as $mimeType) {
136 3
                    $mimeTypes[] = $mimeType;
137
                }
138
            }
139
        }
140
141 16
        return $mimeTypes;
142
    }
143
144 24 View Code Duplication
    private function getRequest(): Request
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...
145
    {
146 24
        $request = $this->requestStack->getCurrentRequest();
147 24
        if (null === $request) {
148
            throw new \RuntimeException('There is no current request.');
149
        }
150
151 24
        return $request;
152
    }
153
}
154
155 1
if (method_exists(BaseNegotiator::class, 'getOrderedElements')) {
156
    /**
157
     * @author Guilhem Niot <[email protected]>
158
     *
159
     * @final since 2.8
160
     */
161
    class FormatNegotiator extends BaseFormatNegotiator
162
    {
163
        public function getBest($header, array $priorities = [], $strict = false)
164
        {
165 24
            return $this->doGetBest($header, $priorities);
166
        }
167
    }
168
} else {
169
    /**
170
     * @author Guilhem Niot <[email protected]>
171
     *
172
     * @final since 2.8
173
     */
174
    class FormatNegotiator extends BaseFormatNegotiator
0 ignored issues
show
Comprehensibility Best Practice introduced by
The type FOS\RestBundle\Negotiation\FormatNegotiator has been defined more than once; this definition is ignored, only the first definition in this file (L161-167) is considered.

This check looks for classes that have been defined more than once in the same file.

If you can, we would recommend to use standard object-oriented programming techniques. For example, to avoid multiple types, it might make sense to create a common interface, and then multiple, different implementations for that interface.

This also has the side-effect of providing you with better IDE auto-completion, static analysis and also better OPCode caching from PHP.

Loading history...
175
    {
176
        public function getBest($header, array $priorities = [])
177
        {
178
            return $this->doGetBest($header, $priorities);
179
        }
180
    }
181
}
182