ParamFetcher   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 168
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 86.08%

Importance

Changes 0
Metric Value
wmc 27
lcom 1
cbo 9
dl 0
loc 168
ccs 68
cts 79
cp 0.8608
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A setController() 0 4 1
A getParams() 0 4 1
A addParam() 0 4 1
A get() 0 18 3
B cleanParamWithRequirements() 0 39 8
A all() 0 11 2
A checkNotIncompatibleParams() 0 24 5
A resolveConstraints() 0 8 3
A getRequest() 0 9 2
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
final class ParamFetcher implements ParamFetcherInterface
37
{
38
    use ResolverTrait;
39
40
    private $container;
41
    private $parameterBag;
42
    private $requestStack;
43
    private $validator;
44
45 36
    public function __construct(ContainerInterface $container, ParamReaderInterface $paramReader, RequestStack $requestStack, ValidatorInterface $validator)
46
    {
47 36
        $this->container = $container;
48 36
        $this->requestStack = $requestStack;
49 36
        $this->validator = $validator;
50
51 36
        $this->parameterBag = new ParameterBag($paramReader);
52 36
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 34
    public function setController(callable $controller): void
58
    {
59 34
        $this->parameterBag->setController($this->getRequest(), $controller);
60 34
    }
61
62
    /**
63
     * Add additional params to the ParamFetcher during runtime.
64
     *
65
     * Note that adding a param that has the same name as an existing param will override that param.
66
     */
67 1
    public function addParam(ParamInterface $param): void
68
    {
69 1
        $this->parameterBag->addParam($this->getRequest(), $param);
70 1
    }
71
72
    /**
73
     * @return ParamInterface[]
74
     */
75 35
    public function getParams(): array
76
    {
77 35
        return $this->parameterBag->getParams($this->getRequest());
78
    }
79
80
    /**
81
     * {@inheritdoc}
82
     */
83 30
    public function get(string $name, ?bool $strict = null)
84
    {
85 30
        $params = $this->getParams();
86
87 30
        if (!array_key_exists($name, $params)) {
88 1
            throw new \InvalidArgumentException(sprintf("No @ParamInterface configuration for parameter '%s'.", $name));
89
        }
90
91
        /** @var ParamInterface $param */
92 29
        $param = $params[$name];
93 29
        $default = $param->getDefault();
94 29
        $default = $this->resolveValue($this->container, $default);
95 29
        $strict = (null !== $strict ? $strict : $param->isStrict());
96
97 29
        $paramValue = $param->getValue($this->getRequest(), $default);
98
99 29
        return $this->cleanParamWithRequirements($param, $paramValue, $strict, $default);
100
    }
101
102 29
    private function cleanParamWithRequirements(ParamInterface $param, $paramValue, bool $strict, $default)
103
    {
104 29
        $this->checkNotIncompatibleParams($param);
105 27
        if (null !== $default && $default === $paramValue) {
106 6
            return $paramValue;
107
        }
108
109 25
        $constraints = $param->getConstraints();
110 25
        $this->resolveConstraints($constraints);
111 25
        if (empty($constraints)) {
112 3
            return $paramValue;
113
        }
114
115
        try {
116 22
            $errors = $this->validator->validate($paramValue, $constraints);
117
        } catch (ValidatorException $e) {
118
            $violation = new ConstraintViolation(
119
                $e->getMessage(),
120
                $e->getMessage(),
121
                array(),
122
                $paramValue,
123
                '',
124
                null,
125
                null,
126
                $e->getCode()
127
            );
128
            $errors = new ConstraintViolationList(array($violation));
129
        }
130
131 22
        if (0 < count($errors)) {
132 11
            if ($strict) {
133 2
                throw InvalidParameterException::withViolations($param, $errors);
134
            }
135
136 9
            return null === $default ? '' : $default;
137
        }
138
139 17
        return $paramValue;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145 18
    public function all(?bool $strict = null): array
146
    {
147 18
        $configuredParams = $this->getParams();
148
149 14
        $params = [];
150 14
        foreach ($configuredParams as $name => $param) {
151 14
            $params[$name] = $this->get($name, $strict);
152
        }
153
154 13
        return $params;
155
    }
156
157 29
    private function checkNotIncompatibleParams(ParamInterface $param): void
158
    {
159 29
        if (null === $param->getValue($this->getRequest(), null)) {
160 14
            return;
161
        }
162
163 20
        $params = $this->getParams();
164 20
        foreach ($param->getIncompatibilities() as $incompatibleParamName) {
165 3
            if (!array_key_exists($incompatibleParamName, $params)) {
166 1
                throw new \InvalidArgumentException(sprintf("No @ParamInterface configuration for parameter '%s'.", $incompatibleParamName));
167
            }
168 2
            $incompatibleParam = $params[$incompatibleParamName];
169
170 2
            if (null !== $incompatibleParam->getValue($this->getRequest(), null)) {
171 2
                $exceptionMessage = sprintf(
172 2
                    '"%s" param is incompatible with %s param.',
173 2
                    $param->getName(),
174 2
                    $incompatibleParam->getName()
175
                );
176
177 2
                throw new BadRequestHttpException($exceptionMessage);
178
            }
179
        }
180 18
    }
181
182
    /**
183
     * @param Constraint[] $constraints
184
     */
185 25
    private function resolveConstraints(array $constraints): void
186
    {
187 25
        foreach ($constraints as $constraint) {
188 22
            if ($constraint instanceof ResolvableConstraintInterface) {
189 6
                $constraint->resolve($this->container);
190
            }
191
        }
192 25
    }
193
194 35
    private function getRequest(): Request
195
    {
196 35
        $request = $this->requestStack->getCurrentRequest();
197 35
        if (null === $request) {
198
            throw new \RuntimeException('There is no current request.');
199
        }
200
201 35
        return $request;
202
    }
203
}
204