Completed
Push — master ( c2ab64...a85f6d )
by Guilh
10:49
created

ParamFetcher::cleanParamWithRequirements()   D

Complexity

Conditions 9
Paths 11

Size

Total Lines 44
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 9

Importance

Changes 4
Bugs 1 Features 0
Metric Value
c 4
b 1
f 0
dl 0
loc 44
ccs 30
cts 30
cp 1
rs 4.909
cc 9
eloc 29
nc 11
nop 4
crap 9
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\Request;
13
14
use FOS\RestBundle\Controller\Annotations\ParamInterface;
15
use FOS\RestBundle\Exception\InvalidParameterException;
16
use FOS\RestBundle\Util\ResolverTrait;
17
use FOS\RestBundle\Validator\Constraints\ResolvableConstraintInterface;
18
use Symfony\Component\DependencyInjection\ContainerInterface;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\RequestStack;
21
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
22
use Symfony\Component\Validator\Constraint;
23
use Symfony\Component\Validator\ConstraintViolationList;
24
use Symfony\Component\Validator\Validator\ValidatorInterface;
25
use Symfony\Component\Validator\Exception\ValidatorException;
26
use Symfony\Component\Validator\ConstraintViolation;
27
28
/**
29
 * Helper to validate parameters of the active request.
30
 *
31
 * @author Alexander <[email protected]>
32
 * @author Lukas Kahwe Smith <[email protected]>
33
 * @author Jordi Boggiano <[email protected]>
34
 * @author Boris Guéry <[email protected]>
35
 */
36
class ParamFetcher implements ParamFetcherInterface
37
{
38
    use ResolverTrait;
39
40
    private $container;
41
    private $parameterBag;
42
    private $requestStack;
43
    private $validator;
44
45
    /**
46
     * Initializes fetcher.
47
     *
48
     * @param ContainerInterface   $container
49
     * @param ParamReaderInterface $paramReader
50
     * @param RequestStack         $requestStack
51
     * @param ValidatorInterface   $validator
52
     */
53 28
    public function __construct(ContainerInterface $container, ParamReaderInterface $paramReader, RequestStack $requestStack, ValidatorInterface $validator = null)
54
    {
55 28
        $this->container = $container;
56 28
        $this->requestStack = $requestStack;
57 28
        $this->validator = $validator;
58
59 28
        $this->parameterBag = new ParameterBag($paramReader);
60 28
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 16
    public function setController($controller)
66
    {
67 16
        $this->parameterBag->setController($this->getRequest(), $controller);
68 16
    }
69
70
    /**
71
     * Add additional params to the ParamFetcher during runtime.
72
     *
73
     * Note that adding a param that has the same name as an existing param will override that param.
74
     *
75
     * @param ParamInterface $param
76
     */
77 1
    public function addParam(ParamInterface $param)
78
    {
79 1
        $this->parameterBag->addParam($this->getRequest(), $param);
80 1
    }
81
82
    /**
83
     * @return ParamInterface[]
84
     */
85 16
    public function getParams()
86
    {
87 16
        return $this->parameterBag->getParams($this->getRequest());
88
    }
89
90
    /**
91
     * {@inheritdoc}
92
     */
93 13
    public function get($name, $strict = null)
94
    {
95 13
        $params = $this->getParams();
96
97 13
        if (!array_key_exists($name, $params)) {
98 1
            throw new \InvalidArgumentException(sprintf("No @ParamInterface configuration for parameter '%s'.", $name));
99
        }
100
101
        /** @var ParamInterface $param */
102 12
        $param = $params[$name];
103 12
        $default = $param->getDefault();
104 12
        $default = $this->resolveValue($this->container, $default);
105
        $strict = (null !== $strict ? $strict : $param->isStrict());
106 12
107
        $paramValue = $param->getValue($this->getRequest(), $default);
108 12
109
        return $this->cleanParamWithRequirements($param, $paramValue, $strict, $default);
110
    }
111
112
    /**
113
     * @param ParamInterface $param
114
     * @param mixed          $paramValue
115
     * @param bool           $strict
116
     * @param mixed          $default
117
     *
118
     * @throws BadRequestHttpException
119
     * @throws \RuntimeException
120
     *
121
     * @return mixed
122
     *
123 17
     * @internal
124
     */
125 17
    protected function cleanParamWithRequirements(ParamInterface $param, $paramValue, $strict, $default)
126 17
    {
127
        $this->checkNotIncompatibleParams($param);
128 17
        if ($default !== null && $default === $paramValue) {
129 17
            return $paramValue;
130 5
        }
131
132
        $constraints = $param->getConstraints();
133 15
        $this->resolveConstraints($constraints);
134 15
        if (empty($constraints)) {
135 15
            return $paramValue;
136 1
        }
137
        if (null === $this->validator) {
138 14
            throw new \RuntimeException(
139 1
                'The ParamFetcher requirements feature requires the symfony/validator component.'
140
            );
141 1
        }
142
143
        try {
144
            $errors = $this->validator->validate($paramValue, $constraints);
145 13
        } catch (ValidatorException $e) {
146 13
            $violation = new ConstraintViolation(
147 3
                $e->getMessage(),
148 3
                $e->getMessage(),
149 3
                array(),
150 3
                $paramValue,
151 3
                '',
152 3
                null,
153 3
                null,
154 3
                $e->getCode()
155 3
            );
156 3
            $errors = new ConstraintViolationList(array($violation));
157 3
        }
158
159
        if (0 < count($errors)) {
160 13
            if ($strict) {
161 9
                throw InvalidParameterException::withViolations($param, $errors);
162 2
            }
163
164
            return null === $default ? '' : $default;
165 7
        }
166
167
        return $paramValue;
168 9
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173
    public function all($strict = null)
174 10
    {
175
        $configuredParams = $this->getParams();
176 10
177
        $params = [];
178 6
        foreach ($configuredParams as $name => $param) {
179 6
            $params[$name] = $this->get($name, $strict);
180 6
        }
181 6
182
        return $params;
183 6
    }
184
185
    /**
186
     * Check if current param is not in conflict with other parameters
187
     * according to the "incompatibles" field.
188
     *
189
     * @param ParamInterface $param the configuration for the param fetcher
190
     *
191
     * @throws InvalidArgumentException
192
     * @throws BadRequestHttpException
193
     *
194
     * @internal
195
     */
196
    protected function checkNotIncompatibleParams(ParamInterface $param)
197 12
    {
198
        $params = $this->getParams();
199 12
        foreach ($param->getIncompatibilities() as $incompatibleParamName) {
200 12
            if (!array_key_exists($incompatibleParamName, $params)) {
201 2
                throw new \InvalidArgumentException(sprintf("No @ParamInterface configuration for parameter '%s'.", $incompatibleParamName));
202 1
            }
203
            $incompatibleParam = $params[$incompatibleParamName];
204 1
205
            if ($incompatibleParam->getValue($this->getRequest(), null) !== null) {
206 1
                $exceptionMessage = sprintf(
207 1
                    "'%s' param is incompatible with %s param.",
208 1
                    $param->getName(),
209 1
                    $incompatibleParam->getName()
210 1
                );
211 1
212
                throw new BadRequestHttpException($exceptionMessage);
213 1
            }
214
        }
215 11
    }
216 10
217
    /**
218
     * @param Constraint[] $constraints
219
     */
220
    private function resolveConstraints(array $constraints)
221 15
    {
222
        foreach ($constraints as $constraint) {
223 15
            if ($constraint instanceof ResolvableConstraintInterface) {
224 14
                $constraint->resolve($this->container);
225
            }
226
        }
227 15
    }
228 15
229
    /**
230
     * @throws \RuntimeException
231
     *
232
     * @return Request
233
     */
234 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...
235 19
    {
236
        $request = $this->requestStack->getCurrentRequest();
237 19
        if ($request === null) {
238 19
            throw new \RuntimeException('There is no current request.');
239
        }
240
241
        return $request;
242 19
    }
243
}
244