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() |
|
|
|
|
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
|
|
|
|
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.