This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | namespace GuzzleHttp\Command\Guzzle; |
||
3 | |||
4 | use GuzzleHttp\Command\CommandInterface; |
||
5 | use GuzzleHttp\Command\Guzzle\ResponseLocation\BodyLocation; |
||
6 | use GuzzleHttp\Command\Guzzle\ResponseLocation\HeaderLocation; |
||
7 | use GuzzleHttp\Command\Guzzle\ResponseLocation\JsonLocation; |
||
8 | use GuzzleHttp\Command\Guzzle\ResponseLocation\ReasonPhraseLocation; |
||
9 | use GuzzleHttp\Command\Guzzle\ResponseLocation\ResponseLocationInterface; |
||
10 | use GuzzleHttp\Command\Guzzle\ResponseLocation\StatusCodeLocation; |
||
11 | use GuzzleHttp\Command\Guzzle\ResponseLocation\XmlLocation; |
||
12 | use GuzzleHttp\Command\Result; |
||
13 | use GuzzleHttp\Command\ResultInterface; |
||
14 | use Psr\Http\Message\RequestInterface; |
||
15 | use Psr\Http\Message\ResponseInterface; |
||
16 | |||
17 | /** |
||
18 | * Handler used to create response models based on an HTTP response and |
||
19 | * a service description. |
||
20 | * |
||
21 | * Response location visitors are registered with this Handler to handle |
||
22 | * locations (e.g., 'xml', 'json', 'header'). All of the locations of a response |
||
23 | * model that will be visited first have their ``before`` method triggered. |
||
24 | * After the before method is called on every visitor that will be walked, each |
||
25 | * visitor is triggered using the ``visit()`` method. After all of the visitors |
||
26 | * are visited, the ``after()`` method is called on each visitor. This is the |
||
27 | * place in which you should handle things like additionalProperties with |
||
28 | * custom locations (i.e., this is how it is handled in the JSON visitor). |
||
29 | */ |
||
30 | class Deserializer |
||
31 | { |
||
32 | /** @var ResponseLocationInterface[] $responseLocations */ |
||
33 | private $responseLocations; |
||
34 | |||
35 | /** @var DescriptionInterface $description */ |
||
36 | private $description; |
||
37 | |||
38 | /** @var boolean $process */ |
||
39 | private $process; |
||
40 | |||
41 | /** |
||
42 | * @param DescriptionInterface $description |
||
43 | * @param bool $process |
||
44 | * @param ResponseLocationInterface[] $responseLocations Extra response locations |
||
45 | */ |
||
46 | 16 | public function __construct( |
|
47 | DescriptionInterface $description, |
||
48 | $process, |
||
49 | array $responseLocations = [] |
||
50 | ) { |
||
51 | 16 | static $defaultResponseLocations; |
|
52 | 16 | if (!$defaultResponseLocations) { |
|
53 | $defaultResponseLocations = [ |
||
54 | 1 | 'body' => new BodyLocation(), |
|
55 | 1 | 'header' => new HeaderLocation(), |
|
56 | 1 | 'reasonPhrase' => new ReasonPhraseLocation(), |
|
57 | 1 | 'statusCode' => new StatusCodeLocation(), |
|
58 | 1 | 'xml' => new XmlLocation(), |
|
59 | 1 | 'json' => new JsonLocation(), |
|
60 | 1 | ]; |
|
61 | 1 | } |
|
62 | |||
63 | 16 | $this->responseLocations = $responseLocations + $defaultResponseLocations; |
|
0 ignored issues
–
show
|
|||
64 | 16 | $this->description = $description; |
|
65 | 16 | $this->process = $process; |
|
66 | 16 | } |
|
67 | |||
68 | /** |
||
69 | * Deserialize the response into the specified result representation |
||
70 | * |
||
71 | * @param ResponseInterface $response |
||
72 | * @param RequestInterface|null $request |
||
73 | * @param CommandInterface $command |
||
74 | * @return Result|ResultInterface|void|ResponseInterface |
||
75 | */ |
||
76 | 15 | public function __invoke(ResponseInterface $response, RequestInterface $request, CommandInterface $command) |
|
77 | { |
||
78 | // If the user don't want to process the result, just return the plain response here |
||
79 | 15 | if ($this->process === false) { |
|
80 | return $response; |
||
81 | } |
||
82 | |||
83 | 15 | $name = $command->getName(); |
|
84 | 15 | $operation = $this->description->getOperation($name); |
|
85 | |||
86 | 15 | $this->handleErrorResponses($response, $request, $command, $operation); |
|
87 | |||
88 | // Add a default Model as the result if no matching schema was found |
||
89 | 12 | if (!($modelName = $operation->getResponseModel())) { |
|
90 | // Not sure if this should be empty or contains the response. |
||
91 | // Decided to do it how it was in the old version for now. |
||
92 | return new Result(); |
||
93 | } |
||
94 | |||
95 | 12 | $model = $operation->getServiceDescription()->getModel($modelName); |
|
96 | 12 | if (!$model) { |
|
97 | throw new \RuntimeException("Unknown model: {$modelName}"); |
||
98 | } |
||
99 | |||
100 | 12 | return $this->visit($model, $response); |
|
101 | } |
||
102 | |||
103 | /** |
||
104 | * Handles visit() and after() methods of the Response locations |
||
105 | * |
||
106 | * @param Parameter $model |
||
107 | * @param ResponseInterface $response |
||
108 | * @return Result|ResultInterface|void |
||
109 | */ |
||
110 | 12 | protected function visit(Parameter $model, ResponseInterface $response) |
|
111 | { |
||
112 | 12 | $result = new Result(); |
|
113 | 12 | $context = ['visitors' => []]; |
|
114 | |||
115 | 12 | if ($model->getType() === 'object') { |
|
116 | 10 | $result = $this->visitOuterObject($model, $result, $response, $context); |
|
117 | 12 | } elseif ($model->getType() === 'array') { |
|
118 | 2 | $result = $this->visitOuterArray($model, $result, $response, $context); |
|
119 | 2 | } else { |
|
120 | throw new \InvalidArgumentException('Invalid response model: ' . $model->getType()); |
||
121 | } |
||
122 | |||
123 | // Call the after() method of each found visitor |
||
124 | /** @var ResponseLocationInterface $visitor */ |
||
125 | 12 | foreach ($context['visitors'] as $visitor) { |
|
126 | 11 | $result = $visitor->after($result, $response, $model); |
|
127 | 12 | } |
|
128 | |||
129 | 12 | return $result; |
|
130 | } |
||
131 | |||
132 | /** |
||
133 | * Handles the before() method of Response locations |
||
134 | * |
||
135 | * @param string $location |
||
136 | * @param Parameter $model |
||
137 | * @param ResultInterface $result |
||
138 | * @param ResponseInterface $response |
||
139 | * @param array $context |
||
140 | * @return ResultInterface |
||
141 | */ |
||
142 | 11 | private function triggerBeforeVisitor( |
|
143 | $location, |
||
144 | Parameter $model, |
||
145 | ResultInterface $result, |
||
146 | ResponseInterface $response, |
||
147 | array &$context |
||
148 | ) { |
||
149 | 11 | if (!isset($this->responseLocations[$location])) { |
|
150 | throw new \RuntimeException("Unknown location: $location"); |
||
151 | } |
||
152 | |||
153 | 11 | $context['visitors'][$location] = $this->responseLocations[$location]; |
|
154 | |||
155 | 11 | $result = $this->responseLocations[$location]->before( |
|
156 | 11 | $result, |
|
157 | 11 | $response, |
|
158 | $model |
||
159 | 11 | ); |
|
160 | |||
161 | 11 | return $result; |
|
162 | } |
||
163 | |||
164 | /** |
||
165 | * Visits the outer object |
||
166 | * |
||
167 | * @param Parameter $model |
||
168 | * @param ResultInterface $result |
||
169 | * @param ResponseInterface $response |
||
170 | * @param array $context |
||
171 | * @return ResultInterface |
||
172 | */ |
||
173 | 10 | private function visitOuterObject( |
|
174 | Parameter $model, |
||
175 | ResultInterface $result, |
||
176 | ResponseInterface $response, |
||
177 | array &$context |
||
178 | ) { |
||
179 | 10 | $parentLocation = $model->getLocation(); |
|
180 | |||
181 | // If top-level additionalProperties is a schema, then visit it |
||
182 | 10 | $additional = $model->getAdditionalProperties(); |
|
183 | 10 | if ($additional instanceof Parameter) { |
|
184 | // Use the model location if none set on additionalProperties. |
||
185 | 4 | $location = $additional->getLocation() ?: $parentLocation; |
|
186 | 4 | $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context); |
|
187 | 4 | } |
|
188 | |||
189 | // Use 'location' from all individual defined properties, but fall back |
||
190 | // to the model location if no per-property location is set. Collect |
||
191 | // the properties that need to be visited into an array. |
||
192 | 10 | $visitProperties = []; |
|
193 | 10 | foreach ($model->getProperties() as $schema) { |
|
194 | 7 | $location = $schema->getLocation() ?: $parentLocation; |
|
195 | 7 | if ($location) { |
|
0 ignored issues
–
show
The expression
$location of type string|null is loosely compared to true ; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.
In PHP, under loose comparison (like For '' == false // true
'' == null // true
'ab' == false // false
'ab' == null // false
// It is often better to use strict comparison
'' === false // false
'' === null // false
![]() |
|||
196 | 7 | $visitProperties[] = [$location, $schema]; |
|
197 | // Trigger the before method on each unique visitor location |
||
198 | 7 | View Code Duplication | if (!isset($context['visitors'][$location])) { |
199 | 5 | $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context); |
|
200 | 5 | } |
|
201 | 7 | } |
|
202 | 10 | } |
|
203 | |||
204 | // Actually visit each response element |
||
205 | 10 | foreach ($visitProperties as $property) { |
|
206 | 7 | $result = $this->responseLocations[$property[0]]->visit($result, $response, $property[1]); |
|
207 | 10 | } |
|
208 | |||
209 | 10 | return $result; |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * Visits the outer array |
||
214 | * |
||
215 | * @param Parameter $model |
||
216 | * @param ResultInterface $result |
||
217 | * @param ResponseInterface $response |
||
218 | * @param array $context |
||
219 | * @return ResultInterface|void |
||
220 | */ |
||
221 | 2 | private function visitOuterArray( |
|
222 | Parameter $model, |
||
223 | ResultInterface $result, |
||
224 | ResponseInterface $response, |
||
225 | array &$context |
||
226 | ) { |
||
227 | // Use 'location' defined on the top of the model |
||
228 | 2 | if (!($location = $model->getLocation())) { |
|
229 | return; |
||
230 | } |
||
231 | |||
232 | // Trigger the before method on each unique visitor location |
||
233 | 2 | View Code Duplication | if (!isset($context['visitors'][$location])) { |
234 | 2 | $result = $this->triggerBeforeVisitor($location, $model, $result, $response, $context); |
|
235 | 2 | } |
|
236 | |||
237 | // Visit each item in the response |
||
238 | 2 | $result = $this->responseLocations[$location]->visit($result, $response, $model); |
|
239 | |||
240 | 2 | return $result; |
|
241 | } |
||
242 | |||
243 | /** |
||
244 | * Reads the "errorResponses" from commands, and trigger appropriate exceptions |
||
245 | * |
||
246 | * In order for the exception to be properly triggered, all your exceptions must be instance |
||
247 | * of "GuzzleHttp\Command\Exception\CommandException". If that's not the case, your exceptions will be wrapped |
||
248 | * around a CommandException |
||
249 | * |
||
250 | * @param ResponseInterface $response |
||
251 | * @param RequestInterface $request |
||
252 | * @param CommandInterface $command |
||
253 | * @param Operation $operation |
||
254 | */ |
||
255 | 15 | protected function handleErrorResponses( |
|
256 | ResponseInterface $response, |
||
257 | RequestInterface $request, |
||
258 | CommandInterface $command, |
||
259 | Operation $operation |
||
260 | ) { |
||
261 | 15 | $errors = $operation->getErrorResponses(); |
|
262 | |||
263 | // We iterate through each errors in service description. If the descriptor contains both a phrase and |
||
264 | // status code, there must be an exact match of both. Otherwise, a match of status code is enough |
||
265 | 15 | $bestException = null; |
|
266 | |||
267 | 15 | foreach ($errors as $error) { |
|
268 | 4 | $code = (int) $error['code']; |
|
269 | |||
270 | 4 | if ($response->getStatusCode() !== $code) { |
|
271 | 1 | continue; |
|
272 | } |
||
273 | |||
274 | 3 | if (isset($error['phrase']) && ! ($error['phrase'] === $response->getReasonPhrase())) { |
|
275 | continue; |
||
276 | } |
||
277 | |||
278 | 3 | $bestException = $error['class']; |
|
279 | |||
280 | // If there is an exact match of phrase + code, then we cannot find a more specialized exception in |
||
281 | // the array, so we can break early instead of iterating the remaining ones |
||
282 | 3 | if (isset($error['phrase'])) { |
|
283 | 2 | break; |
|
284 | } |
||
285 | 15 | } |
|
286 | |||
287 | 15 | if (null !== $bestException) { |
|
288 | 3 | throw new $bestException($response->getReasonPhrase(), $command, null, $request, $response); |
|
289 | } |
||
290 | |||
291 | // If we reach here, no exception could be match from descriptor, and Guzzle exception will propagate if |
||
292 | // option "http_errors" is set to true, which is the default setting. |
||
293 | 12 | } |
|
294 | } |
||
295 |
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..