Completed
Pull Request — master (#1506)
by Guilh
15:57 queued 11:18
created

FormatNegotiator::normalizePriorities()   C

Complexity

Conditions 7
Paths 11

Size

Total Lines 28
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 7.3387

Importance

Changes 4
Bugs 2 Features 1
Metric Value
c 4
b 2
f 1
dl 0
loc 28
ccs 17
cts 21
cp 0.8095
rs 6.7272
cc 7
eloc 17
nc 11
nop 2
crap 7.3387
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
class FormatNegotiator extends BaseNegotiator
25
{
26
    private $map = [];
27
    private $requestStack;
28
29 20
    public function __construct(RequestStack $requestStack, array $mimeTypes = array())
30
    {
31 20
        $this->requestStack = $requestStack;
32 20
        $this->mimeTypes = $mimeTypes;
0 ignored issues
show
Bug introduced by
The property mimeTypes does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
33 20
    }
34
35
    /**
36
     * @param RequestMatcherInterface $requestMatcher
37
     * @param array                   $options
38
     */
39 19
    public function add(RequestMatcherInterface $requestMatcher, array $options = [])
40
    {
41 19
        $this->map[] = [$requestMatcher, $options];
42 19
    }
43
44
    /**
45
     * {@inheritdoc}
46
     * The best format is also determined in function of the bundle configuration.
47
     *
48
     * @throws StopFormatListenerException
49
     */
50 17
    public function getBest($header, array $priorities = [])
51
    {
52 17
        $request = $this->getRequest();
53 17
        $header = $header ?: $request->headers->get('Accept');
54
55 17
        foreach ($this->map as $elements) {
56
            // Check if the current RequestMatcherInterface matches the current request
57 16
            if (!$elements[0]->matches($request)) {
58 1
                continue;
59
            }
60 16
            $options = &$elements[1]; // Do not reallow memory for this variable
61
62 16
            if (!empty($options['stop'])) {
63 1
                throw new StopFormatListenerException('Stopped format listener');
64
            }
65 15
            if (empty($options['priorities']) && empty($priorities)) {
66 2
                if (!empty($options['fallback_format'])) {
67 2
                    return new Accept($request->getMimeType($options['fallback_format']));
68
                }
69 1
                continue;
70
            }
71
72 13
            if (isset($options['prefer_extension']) && $options['prefer_extension'] && !isset($extensionHeader)) {
73 8
                $extension = pathinfo($request->getPathInfo(), PATHINFO_EXTENSION);
74
75 8
                if (!empty($extension)) {
76
                    // $extensionHeader will now be either a non empty string or an empty string
77
                    $extensionHeader = $request->getMimeType($extension);
78
                    if ($header && $extensionHeader) {
79
                        $header .= ',';
80
                    }
81
                    $header .= $extensionHeader.'; q='.$options['prefer_extension'];
82
                }
83 8
            }
84
85 13
            if ($header) {
86 12
                $mimeTypes = $this->normalizePriorities($request,
87 12
                    empty($priorities) ? $options['priorities'] : $priorities
88 12
                );
89
90 12
                $mimeType = parent::getBest($header, $mimeTypes);
91
92 12
                if ($mimeType !== null) {
93 11
                    return $mimeType;
94
                }
95 1
            }
96
97 2
            if (isset($options['fallback_format'])) {
98
                // if false === fallback_format then we fail here instead of considering more rules
99 2
                if (false === $options['fallback_format']) {
100
                    return;
101
                }
102
103
                // stop looking at rules since we have a fallback defined
104 2
                return new Accept($request->getMimeType($options['fallback_format']));
105
            }
106 3
        }
107 3
    }
108
109
    /**
110
     * @param array $values
111
     *
112
     * @return array
113
     */
114
    private function sanitize(array $values)
115
    {
116 12
        return array_map(function ($value) {
117 12
            return preg_replace('/\s+/', '', strtolower($value));
118 12
        }, $values);
119
    }
120
121
    /**
122
     * Transform the format (json, html, ...) to their mimeType form (application/json, text/html, ...).
123
     *
124
     * @param Request  $request
125
     * @param string[] $priorities
126
     *
127
     * @return string[] formatted priorities
128
     */
129 12
    private function normalizePriorities(Request $request, array $priorities)
130
    {
131 12
        $priorities = $this->sanitize($priorities);
132
133 12
        $mimeTypes = array();
134 12
        foreach ($priorities as $priority) {
135 12
            if (strpos($priority, '/')) {
136 2
                $mimeTypes[] = $priority;
137 2
                continue;
138
            }
139
140 11
            if (method_exists(Request::class, 'getMimeTypes')) {
141 11
                $mimeTypes = array_merge($mimeTypes, Request::getMimeTypes($priority));
0 ignored issues
show
Bug introduced by
The method getMimeTypes() does not exist on Symfony\Component\HttpFoundation\Request. Did you maybe mean getMimeType()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
142 11
            } elseif (null !== $request->getMimeType($priority)) {
143
                $class = new \ReflectionClass(Request::class);
144
                $properties = $class->getStaticProperties();
145
                $mimeTypes = array_merge($mimeTypes, $properties['formats'][$priority]);
146
            }
147
148 11
            if (isset($this->mimeTypes[$priority])) {
149 3
                foreach ($this->mimeTypes[$priority] as $mimeType) {
150 3
                    $mimeTypes[] = $mimeType;
151 3
                }
152 3
            }
153 12
        }
154
155 12
        return $mimeTypes;
156
    }
157
158
    /**
159
     * @throws \RuntimeException
160
     *
161
     * @return Request
162
     */
163 17 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...
164
    {
165 17
        $request = $this->requestStack->getCurrentRequest();
166 17
        if ($request === null) {
167
            throw new \RuntimeException('There is no current request.');
168
        }
169
170 17
        return $request;
171
    }
172
}
173