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 | |||
3 | /* |
||
4 | * This file is part of the Behat WebApiExtension. |
||
5 | * (c) Konstantin Kudryashov <[email protected]> |
||
6 | * |
||
7 | * For the full copyright and license information, please view the LICENSE |
||
8 | * file that was distributed with this source code. |
||
9 | */ |
||
10 | |||
11 | namespace Behat\WebApiExtension\Context; |
||
12 | |||
13 | use Behat\Gherkin\Node\PyStringNode; |
||
14 | use Behat\Gherkin\Node\TableNode; |
||
15 | use GuzzleHttp\ClientInterface; |
||
16 | use GuzzleHttp\Exception\RequestException; |
||
17 | use GuzzleHttp\Psr7\Request; |
||
18 | use PHPUnit_Framework_Assert as Assertions; |
||
19 | use Psr\Http\Message\RequestInterface; |
||
20 | use Psr\Http\Message\ResponseInterface; |
||
21 | |||
22 | /** |
||
23 | * Provides web API description definitions. |
||
24 | * |
||
25 | * @author Konstantin Kudryashov <[email protected]> |
||
26 | */ |
||
27 | class WebApiContext implements ApiClientAwareContext |
||
28 | { |
||
29 | /** |
||
30 | * @var string |
||
31 | */ |
||
32 | private $authorization; |
||
33 | |||
34 | /** |
||
35 | * @var ClientInterface |
||
36 | */ |
||
37 | private $client; |
||
38 | |||
39 | /** |
||
40 | * @var array |
||
41 | */ |
||
42 | private $headers = array(); |
||
43 | |||
44 | /** |
||
45 | * @var \GuzzleHttp\Message\RequestInterface|RequestInterface |
||
46 | */ |
||
47 | private $request; |
||
48 | |||
49 | /** |
||
50 | * @var \GuzzleHttp\Message\ResponseInterface|ResponseInterface |
||
51 | */ |
||
52 | private $response; |
||
53 | |||
54 | private $placeHolders = array(); |
||
55 | |||
56 | /** |
||
57 | * {@inheritdoc} |
||
58 | */ |
||
59 | public function setClient(ClientInterface $client) |
||
60 | { |
||
61 | $this->client = $client; |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * Adds Basic Authentication header to next request. |
||
66 | * |
||
67 | * @param string $username |
||
68 | * @param string $password |
||
69 | * |
||
70 | * @Given /^I am authenticating as "([^"]*)" with "([^"]*)" password$/ |
||
71 | */ |
||
72 | public function iAmAuthenticatingAs($username, $password) |
||
73 | { |
||
74 | $this->removeHeader('Authorization'); |
||
75 | $this->authorization = base64_encode($username . ':' . $password); |
||
76 | $this->addHeader('Authorization', 'Basic ' . $this->authorization); |
||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Sets a HTTP Header. |
||
81 | * |
||
82 | * @param string $name header name |
||
83 | * @param string $value header value |
||
84 | * |
||
85 | * @Given /^I set header "([^"]*)" with value "([^"]*)"$/ |
||
86 | */ |
||
87 | public function iSetHeaderWithValue($name, $value) |
||
88 | { |
||
89 | $this->addHeader($name, $value); |
||
90 | } |
||
91 | |||
92 | /** |
||
93 | * Sends HTTP request to specific relative URL. |
||
94 | * |
||
95 | * @param string $method request method |
||
96 | * @param string $url relative url |
||
97 | * |
||
98 | * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)"$/ |
||
99 | */ |
||
100 | public function iSendARequest($method, $url) |
||
101 | { |
||
102 | $url = $this->prepareUrl($url); |
||
103 | |||
104 | View Code Duplication | if (version_compare(ClientInterface::VERSION, '6.0', '>=')) { |
|
0 ignored issues
–
show
|
|||
105 | $this->request = new Request($method, $url, $this->headers); |
||
106 | } else { |
||
107 | $this->request = $this->getClient()->createRequest($method, $url); |
||
0 ignored issues
–
show
The method
createRequest() does not exist on GuzzleHttp\ClientInterface . Did you maybe mean request() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
108 | if (!empty($this->headers)) { |
||
109 | $this->request->addHeaders($this->headers); |
||
110 | } |
||
111 | } |
||
112 | |||
113 | $this->sendRequest(); |
||
114 | } |
||
115 | |||
116 | /** |
||
117 | * Sends HTTP request to specific URL with field values from Table. |
||
118 | * |
||
119 | * @param string $method request method |
||
120 | * @param string $url relative url |
||
121 | * @param TableNode $post table of post values |
||
122 | * |
||
123 | * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with values:$/ |
||
124 | */ |
||
125 | public function iSendARequestWithValues($method, $url, TableNode $post) |
||
126 | { |
||
127 | $url = $this->prepareUrl($url); |
||
128 | $fields = array(); |
||
129 | |||
130 | foreach ($post->getRowsHash() as $key => $val) { |
||
131 | $fields[$key] = $this->replacePlaceHolder($val); |
||
132 | } |
||
133 | |||
134 | $bodyOption = array( |
||
135 | 'body' => json_encode($fields), |
||
136 | ); |
||
137 | |||
138 | View Code Duplication | if (version_compare(ClientInterface::VERSION, '6.0', '>=')) { |
|
0 ignored issues
–
show
This code seems to be duplicated across 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. ![]() |
|||
139 | $this->request = new Request($method, $url, $this->headers, $bodyOption['body']); |
||
140 | } else { |
||
141 | $this->request = $this->getClient()->createRequest($method, $url, $bodyOption); |
||
0 ignored issues
–
show
The method
createRequest() does not exist on GuzzleHttp\ClientInterface . Did you maybe mean request() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
142 | if (!empty($this->headers)) { |
||
143 | $this->request->addHeaders($this->headers); |
||
144 | } |
||
145 | } |
||
146 | |||
147 | $this->sendRequest(); |
||
148 | } |
||
149 | |||
150 | /** |
||
151 | * Sends HTTP request to specific URL with raw body from PyString. |
||
152 | * |
||
153 | * @param string $method request method |
||
154 | * @param string $url relative url |
||
155 | * @param PyStringNode $string request body |
||
156 | * |
||
157 | * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with body:$/ |
||
158 | */ |
||
159 | public function iSendARequestWithBody($method, $url, PyStringNode $string) |
||
160 | { |
||
161 | $url = $this->prepareUrl($url); |
||
162 | $string = $this->replacePlaceHolder(trim($string)); |
||
163 | |||
164 | if (version_compare(ClientInterface::VERSION, '6.0', '>=')) { |
||
165 | $this->request = new Request($method, $url, $this->headers, $string); |
||
166 | } else { |
||
167 | $this->request = $this->getClient()->createRequest( |
||
0 ignored issues
–
show
The method
createRequest() does not exist on GuzzleHttp\ClientInterface . Did you maybe mean request() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
168 | $method, |
||
169 | $url, |
||
170 | array( |
||
171 | 'headers' => $this->getHeaders(), |
||
172 | 'body' => $string, |
||
173 | ) |
||
174 | ); |
||
175 | } |
||
176 | |||
177 | $this->sendRequest(); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Sends HTTP request to specific URL with form data from PyString. |
||
182 | * |
||
183 | * @param string $method request method |
||
184 | * @param string $url relative url |
||
185 | * @param PyStringNode $body request body |
||
186 | * |
||
187 | * @When /^(?:I )?send a ([A-Z]+) request to "([^"]+)" with form data:$/ |
||
188 | */ |
||
189 | public function iSendARequestWithFormData($method, $url, PyStringNode $body) |
||
190 | { |
||
191 | $url = $this->prepareUrl($url); |
||
192 | $body = $this->replacePlaceHolder(trim($body)); |
||
193 | |||
194 | $fields = array(); |
||
195 | parse_str(implode('&', explode("\n", $body)), $fields); |
||
196 | |||
197 | if (version_compare(ClientInterface::VERSION, '6.0', '>=')) { |
||
198 | $this->request = new Request($method, $url, ['Content-Type' => 'application/x-www-form-urlencoded'], http_build_query($fields, null, '&')); |
||
199 | } else { |
||
200 | $this->request = $this->getClient()->createRequest($method, $url); |
||
0 ignored issues
–
show
The method
createRequest() does not exist on GuzzleHttp\ClientInterface . Did you maybe mean request() ?
This check marks calls to methods that do not seem to exist on an object. This is most likely the result of a method being renamed without all references to it being renamed likewise. ![]() |
|||
201 | /** @var \GuzzleHttp\Post\PostBodyInterface $requestBody */ |
||
202 | $requestBody = $this->request->getBody(); |
||
203 | foreach ($fields as $key => $value) { |
||
0 ignored issues
–
show
The expression
$fields of type null|array is not guaranteed to be traversable. How about adding an additional type check?
There are different options of fixing this problem.
![]() |
|||
204 | $requestBody->setField($key, $value); |
||
205 | } |
||
206 | } |
||
207 | |||
208 | $this->sendRequest(); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * Checks that response has specific status code. |
||
213 | * |
||
214 | * @param string $code status code |
||
215 | * |
||
216 | * @Then /^(?:the )?response code should be (\d+)$/ |
||
217 | */ |
||
218 | public function theResponseCodeShouldBe($code) |
||
219 | { |
||
220 | $expected = intval($code); |
||
221 | $actual = intval($this->response->getStatusCode()); |
||
222 | Assertions::assertSame($expected, $actual); |
||
223 | } |
||
224 | |||
225 | /** |
||
226 | * Checks that response body contains specific text. |
||
227 | * |
||
228 | * @param string $text |
||
229 | * |
||
230 | * @Then /^(?:the )?response should contain "([^"]*)"$/ |
||
231 | */ |
||
232 | public function theResponseShouldContain($text) |
||
233 | { |
||
234 | $expectedRegexp = '/' . preg_quote($text) . '/i'; |
||
235 | $actual = (string) $this->response->getBody(); |
||
236 | Assertions::assertRegExp($expectedRegexp, $actual); |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Checks that response body doesn't contains specific text. |
||
241 | * |
||
242 | * @param string $text |
||
243 | * |
||
244 | * @Then /^(?:the )?response should not contain "([^"]*)"$/ |
||
245 | */ |
||
246 | public function theResponseShouldNotContain($text) |
||
247 | { |
||
248 | $expectedRegexp = '/' . preg_quote($text) . '/'; |
||
249 | $actual = (string) $this->response->getBody(); |
||
250 | Assertions::assertNotRegExp($expectedRegexp, $actual); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * Checks that response body contains JSON from PyString. |
||
255 | * |
||
256 | * Do not check that the response body /only/ contains the JSON from PyString, |
||
257 | * |
||
258 | * @param PyStringNode $jsonString |
||
259 | * |
||
260 | * @throws \RuntimeException |
||
261 | * |
||
262 | * @Then /^(?:the )?response should contain json:$/ |
||
263 | */ |
||
264 | public function theResponseShouldContainJson(PyStringNode $jsonString) |
||
265 | { |
||
266 | $etalon = json_decode($this->replacePlaceHolder($jsonString->getRaw()), true); |
||
267 | $actual = json_decode($this->response->getBody(), true); |
||
268 | |||
269 | if (null === $etalon) { |
||
270 | throw new \RuntimeException( |
||
271 | "Can not convert etalon to json:\n" . $this->replacePlaceHolder($jsonString->getRaw()) |
||
272 | ); |
||
273 | } |
||
274 | |||
275 | if (null === $actual) { |
||
276 | throw new \RuntimeException( |
||
277 | "Can not convert actual to json:\n" . $this->replacePlaceHolder((string) $this->response->getBody()) |
||
278 | ); |
||
279 | } |
||
280 | |||
281 | Assertions::assertGreaterThanOrEqual(count($etalon), count($actual)); |
||
282 | foreach ($etalon as $key => $needle) { |
||
283 | Assertions::assertArrayHasKey($key, $actual); |
||
284 | Assertions::assertEquals($etalon[$key], $actual[$key]); |
||
285 | } |
||
286 | } |
||
287 | |||
288 | /** |
||
289 | * Prints last response body. |
||
290 | * |
||
291 | * @Then print response |
||
292 | */ |
||
293 | public function printResponse() |
||
294 | { |
||
295 | $request = $this->request; |
||
296 | $response = $this->response; |
||
297 | |||
298 | echo sprintf( |
||
299 | "%s %s => %d:\n%s", |
||
300 | $request->getMethod(), |
||
301 | (string) ($request instanceof RequestInterface ? $request->getUri() : $request->getUrl()), |
||
302 | $response->getStatusCode(), |
||
303 | (string) $response->getBody() |
||
304 | ); |
||
305 | } |
||
306 | |||
307 | /** |
||
308 | * Prepare URL by replacing placeholders and trimming slashes. |
||
309 | * |
||
310 | * @param string $url |
||
311 | * |
||
312 | * @return string |
||
313 | */ |
||
314 | private function prepareUrl($url) |
||
315 | { |
||
316 | return ltrim($this->replacePlaceHolder($url), '/'); |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * Sets place holder for replacement. |
||
321 | * |
||
322 | * you can specify placeholders, which will |
||
323 | * be replaced in URL, request or response body. |
||
324 | * |
||
325 | * @param string $key token name |
||
326 | * @param string $value replace value |
||
327 | */ |
||
328 | public function setPlaceHolder($key, $value) |
||
329 | { |
||
330 | $this->placeHolders[$key] = $value; |
||
331 | } |
||
332 | |||
333 | /** |
||
334 | * Replaces placeholders in provided text. |
||
335 | * |
||
336 | * @param string $string |
||
337 | * |
||
338 | * @return string |
||
339 | */ |
||
340 | protected function replacePlaceHolder($string) |
||
341 | { |
||
342 | foreach ($this->placeHolders as $key => $val) { |
||
343 | $string = str_replace($key, $val, $string); |
||
344 | } |
||
345 | |||
346 | return $string; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Returns headers, that will be used to send requests. |
||
351 | * |
||
352 | * @return array |
||
353 | */ |
||
354 | protected function getHeaders() |
||
355 | { |
||
356 | return $this->headers; |
||
357 | } |
||
358 | |||
359 | /** |
||
360 | * Adds header |
||
361 | * |
||
362 | * @param string $name |
||
363 | * @param string $value |
||
364 | */ |
||
365 | protected function addHeader($name, $value) |
||
366 | { |
||
367 | if (isset($this->headers[$name])) { |
||
368 | if (!is_array($this->headers[$name])) { |
||
369 | $this->headers[$name] = array($this->headers[$name]); |
||
370 | } |
||
371 | |||
372 | $this->headers[$name][] = $value; |
||
373 | } else { |
||
374 | $this->headers[$name] = $value; |
||
375 | } |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * Removes a header identified by $headerName |
||
380 | * |
||
381 | * @param string $headerName |
||
382 | */ |
||
383 | protected function removeHeader($headerName) |
||
384 | { |
||
385 | if (array_key_exists($headerName, $this->headers)) { |
||
386 | unset($this->headers[$headerName]); |
||
387 | } |
||
388 | } |
||
389 | |||
390 | private function sendRequest() |
||
391 | { |
||
392 | try { |
||
393 | $this->response = $this->getClient()->send($this->request); |
||
394 | } catch (RequestException $e) { |
||
395 | $this->response = $e->getResponse(); |
||
396 | |||
397 | if (null === $this->response) { |
||
398 | throw $e; |
||
399 | } |
||
400 | } |
||
401 | } |
||
402 | |||
403 | private function getClient() |
||
404 | { |
||
405 | if (null === $this->client) { |
||
406 | throw new \RuntimeException('Client has not been set in WebApiContext'); |
||
407 | } |
||
408 | |||
409 | return $this->client; |
||
410 | } |
||
411 | } |
||
412 |
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.