Completed
Pull Request — master (#1576)
by Chad
06:28
created

ParamFetcher::resolveConstraints()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3.2098

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
ccs 5
cts 7
cp 0.7143
rs 9.4285
cc 3
eloc 4
nc 3
nop 1
crap 3.2098
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 12
        $strict = (null !== $strict ? $strict : $param->isStrict());
106
107 12
        $paramValue = $param->getValue($this->getRequest(), $default);
108
109 12
        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
     * @internal
124
     */
125 17
    protected function cleanParamWithRequirements(ParamInterface $param, $paramValue, $strict, $default)
126
    {
127 17
        $this->checkNotIncompatibleParams($param);
128 17
        if ($default !== null && $default === $paramValue) {
129 5
            return $paramValue;
130
        }
131
132 15
        $constraints = $param->getConstraints();
133 15
        $this->resolveConstraints($constraints);
134 15
        if (empty($constraints)) {
135 1
            return $paramValue;
136
        }
137 14
        if (null === $this->validator) {
138 1
            throw new \RuntimeException(
139
                'The ParamFetcher requirements feature requires the symfony/validator component.'
140 1
            );
141
        }
142
143
        try {
144 13
            $errors = $this->validator->validate($paramValue, $constraints);
145 13
        } catch (ValidatorException $e) {
146
            $violation = new ConstraintViolation(
147
                $e->getMessage(),
148
                $e->getMessage(),
149
                array(),
150
                $paramValue,
151
                '',
152
                null,
153
                null,
154
                $e->getCode()
155
            );
156
            $errors = new ConstraintViolationList(array($violation));
157
        }
158
159 13
        if (0 < count($errors)) {
160 7
            if ($strict) {
161 2
                throw InvalidParameterException::withViolations($param, $errors);
162
            }
163
164 5
            return null === $default ? '' : $default;
165
        }
166
167 9
        return $paramValue;
168
    }
169
170
    /**
171
     * {@inheritdoc}
172
     */
173 10
    public function all($strict = null)
174
    {
175 10
        $configuredParams = $this->getParams();
176
177 6
        $params = [];
178 6
        foreach ($configuredParams as $name => $param) {
179 6
            $params[$name] = $this->get($name, $strict);
180 6
        }
181
182 6
        return $params;
183
    }
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 12
    protected function checkNotIncompatibleParams(ParamInterface $param)
197
    {
198 12
        $params = $this->getParams();
199 12
        foreach ($param->getIncompatibilities() as $incompatibleParamName) {
200 2
            if (!array_key_exists($incompatibleParamName, $params)) {
201 1
                throw new \InvalidArgumentException(sprintf("No @ParamInterface configuration for parameter '%s'.", $incompatibleParamName));
202
            }
203 1
            $incompatibleParam = $params[$incompatibleParamName];
204
205 1
            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
212 1
                throw new BadRequestHttpException($exceptionMessage);
213
            }
214 11
        }
215 10
    }
216
217
    /**
218
     * @param Constraint[] $constraints
219
     */
220 15
    private function resolveConstraints(array $constraints)
221
    {
222 15
        foreach ($constraints as $constraint) {
223 14
            if ($constraint instanceof ResolvableConstraintInterface) {
224
                $constraint->resolve($this->container);
225
            }
226 15
        }
227 15
    }
228
229
    /**
230
     * @throws \RuntimeException
231
     *
232
     * @return Request
233
     */
234 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...
235
    {
236 19
        $request = $this->requestStack->getCurrentRequest();
237 19
        if ($request === null) {
238
            throw new \RuntimeException('There is no current request.');
239
        }
240
241 19
        return $request;
242
    }
243
}
244