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; |
|
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) { |
|
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])) { |
0 ignored issues
–
show
|
|||
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 |
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.