Mercurial > hg > isophonics-drupal-site
comparison vendor/symfony/browser-kit/Client.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 /* | |
4 * This file is part of the Symfony package. | |
5 * | |
6 * (c) Fabien Potencier <fabien@symfony.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 Symfony\Component\BrowserKit; | |
13 | |
14 use Symfony\Component\DomCrawler\Crawler; | |
15 use Symfony\Component\DomCrawler\Link; | |
16 use Symfony\Component\DomCrawler\Form; | |
17 use Symfony\Component\Process\PhpProcess; | |
18 | |
19 /** | |
20 * Client simulates a browser. | |
21 * | |
22 * To make the actual request, you need to implement the doRequest() method. | |
23 * | |
24 * If you want to be able to run requests in their own process (insulated flag), | |
25 * you need to also implement the getScript() method. | |
26 * | |
27 * @author Fabien Potencier <fabien@symfony.com> | |
28 */ | |
29 abstract class Client | |
30 { | |
31 protected $history; | |
32 protected $cookieJar; | |
33 protected $server = array(); | |
34 protected $internalRequest; | |
35 protected $request; | |
36 protected $internalResponse; | |
37 protected $response; | |
38 protected $crawler; | |
39 protected $insulated = false; | |
40 protected $redirect; | |
41 protected $followRedirects = true; | |
42 | |
43 private $maxRedirects = -1; | |
44 private $redirectCount = 0; | |
45 private $isMainRequest = true; | |
46 | |
47 /** | |
48 * Constructor. | |
49 * | |
50 * @param array $server The server parameters (equivalent of $_SERVER) | |
51 * @param History $history A History instance to store the browser history | |
52 * @param CookieJar $cookieJar A CookieJar instance to store the cookies | |
53 */ | |
54 public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null) | |
55 { | |
56 $this->setServerParameters($server); | |
57 $this->history = $history ?: new History(); | |
58 $this->cookieJar = $cookieJar ?: new CookieJar(); | |
59 } | |
60 | |
61 /** | |
62 * Sets whether to automatically follow redirects or not. | |
63 * | |
64 * @param bool $followRedirect Whether to follow redirects | |
65 */ | |
66 public function followRedirects($followRedirect = true) | |
67 { | |
68 $this->followRedirects = (bool) $followRedirect; | |
69 } | |
70 | |
71 /** | |
72 * Returns whether client automatically follows redirects or not. | |
73 * | |
74 * @return bool | |
75 */ | |
76 public function isFollowingRedirects() | |
77 { | |
78 return $this->followRedirects; | |
79 } | |
80 | |
81 /** | |
82 * Sets the maximum number of requests that crawler can follow. | |
83 * | |
84 * @param int $maxRedirects | |
85 */ | |
86 public function setMaxRedirects($maxRedirects) | |
87 { | |
88 $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects; | |
89 $this->followRedirects = -1 != $this->maxRedirects; | |
90 } | |
91 | |
92 /** | |
93 * Returns the maximum number of requests that crawler can follow. | |
94 * | |
95 * @return int | |
96 */ | |
97 public function getMaxRedirects() | |
98 { | |
99 return $this->maxRedirects; | |
100 } | |
101 | |
102 /** | |
103 * Sets the insulated flag. | |
104 * | |
105 * @param bool $insulated Whether to insulate the requests or not | |
106 * | |
107 * @throws \RuntimeException When Symfony Process Component is not installed | |
108 */ | |
109 public function insulate($insulated = true) | |
110 { | |
111 if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) { | |
112 throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.'); | |
113 } | |
114 | |
115 $this->insulated = (bool) $insulated; | |
116 } | |
117 | |
118 /** | |
119 * Sets server parameters. | |
120 * | |
121 * @param array $server An array of server parameters | |
122 */ | |
123 public function setServerParameters(array $server) | |
124 { | |
125 $this->server = array_merge(array( | |
126 'HTTP_USER_AGENT' => 'Symfony BrowserKit', | |
127 ), $server); | |
128 } | |
129 | |
130 /** | |
131 * Sets single server parameter. | |
132 * | |
133 * @param string $key A key of the parameter | |
134 * @param string $value A value of the parameter | |
135 */ | |
136 public function setServerParameter($key, $value) | |
137 { | |
138 $this->server[$key] = $value; | |
139 } | |
140 | |
141 /** | |
142 * Gets single server parameter for specified key. | |
143 * | |
144 * @param string $key A key of the parameter to get | |
145 * @param string $default A default value when key is undefined | |
146 * | |
147 * @return string A value of the parameter | |
148 */ | |
149 public function getServerParameter($key, $default = '') | |
150 { | |
151 return isset($this->server[$key]) ? $this->server[$key] : $default; | |
152 } | |
153 | |
154 /** | |
155 * Returns the History instance. | |
156 * | |
157 * @return History A History instance | |
158 */ | |
159 public function getHistory() | |
160 { | |
161 return $this->history; | |
162 } | |
163 | |
164 /** | |
165 * Returns the CookieJar instance. | |
166 * | |
167 * @return CookieJar A CookieJar instance | |
168 */ | |
169 public function getCookieJar() | |
170 { | |
171 return $this->cookieJar; | |
172 } | |
173 | |
174 /** | |
175 * Returns the current Crawler instance. | |
176 * | |
177 * @return Crawler|null A Crawler instance | |
178 */ | |
179 public function getCrawler() | |
180 { | |
181 return $this->crawler; | |
182 } | |
183 | |
184 /** | |
185 * Returns the current BrowserKit Response instance. | |
186 * | |
187 * @return Response|null A BrowserKit Response instance | |
188 */ | |
189 public function getInternalResponse() | |
190 { | |
191 return $this->internalResponse; | |
192 } | |
193 | |
194 /** | |
195 * Returns the current origin response instance. | |
196 * | |
197 * The origin response is the response instance that is returned | |
198 * by the code that handles requests. | |
199 * | |
200 * @return object|null A response instance | |
201 * | |
202 * @see doRequest() | |
203 */ | |
204 public function getResponse() | |
205 { | |
206 return $this->response; | |
207 } | |
208 | |
209 /** | |
210 * Returns the current BrowserKit Request instance. | |
211 * | |
212 * @return Request|null A BrowserKit Request instance | |
213 */ | |
214 public function getInternalRequest() | |
215 { | |
216 return $this->internalRequest; | |
217 } | |
218 | |
219 /** | |
220 * Returns the current origin Request instance. | |
221 * | |
222 * The origin request is the request instance that is sent | |
223 * to the code that handles requests. | |
224 * | |
225 * @return object|null A Request instance | |
226 * | |
227 * @see doRequest() | |
228 */ | |
229 public function getRequest() | |
230 { | |
231 return $this->request; | |
232 } | |
233 | |
234 /** | |
235 * Clicks on a given link. | |
236 * | |
237 * @param Link $link A Link instance | |
238 * | |
239 * @return Crawler | |
240 */ | |
241 public function click(Link $link) | |
242 { | |
243 if ($link instanceof Form) { | |
244 return $this->submit($link); | |
245 } | |
246 | |
247 return $this->request($link->getMethod(), $link->getUri()); | |
248 } | |
249 | |
250 /** | |
251 * Submits a form. | |
252 * | |
253 * @param Form $form A Form instance | |
254 * @param array $values An array of form field values | |
255 * | |
256 * @return Crawler | |
257 */ | |
258 public function submit(Form $form, array $values = array()) | |
259 { | |
260 $form->setValues($values); | |
261 | |
262 return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles()); | |
263 } | |
264 | |
265 /** | |
266 * Calls a URI. | |
267 * | |
268 * @param string $method The request method | |
269 * @param string $uri The URI to fetch | |
270 * @param array $parameters The Request parameters | |
271 * @param array $files The files | |
272 * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does) | |
273 * @param string $content The raw body data | |
274 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) | |
275 * | |
276 * @return Crawler | |
277 */ | |
278 public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true) | |
279 { | |
280 if ($this->isMainRequest) { | |
281 $this->redirectCount = 0; | |
282 } else { | |
283 ++$this->redirectCount; | |
284 } | |
285 | |
286 $uri = $this->getAbsoluteUri($uri); | |
287 | |
288 $server = array_merge($this->server, $server); | |
289 | |
290 if (isset($server['HTTPS'])) { | |
291 $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri); | |
292 } | |
293 | |
294 if (!$this->history->isEmpty()) { | |
295 $server['HTTP_REFERER'] = $this->history->current()->getUri(); | |
296 } | |
297 | |
298 if (empty($server['HTTP_HOST'])) { | |
299 $server['HTTP_HOST'] = $this->extractHost($uri); | |
300 } | |
301 | |
302 $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME); | |
303 | |
304 $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content); | |
305 | |
306 $this->request = $this->filterRequest($this->internalRequest); | |
307 | |
308 if (true === $changeHistory) { | |
309 $this->history->add($this->internalRequest); | |
310 } | |
311 | |
312 if ($this->insulated) { | |
313 $this->response = $this->doRequestInProcess($this->request); | |
314 } else { | |
315 $this->response = $this->doRequest($this->request); | |
316 } | |
317 | |
318 $this->internalResponse = $this->filterResponse($this->response); | |
319 | |
320 $this->cookieJar->updateFromResponse($this->internalResponse, $uri); | |
321 | |
322 $status = $this->internalResponse->getStatus(); | |
323 | |
324 if ($status >= 300 && $status < 400) { | |
325 $this->redirect = $this->internalResponse->getHeader('Location'); | |
326 } else { | |
327 $this->redirect = null; | |
328 } | |
329 | |
330 if ($this->followRedirects && $this->redirect) { | |
331 return $this->crawler = $this->followRedirect(); | |
332 } | |
333 | |
334 return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type')); | |
335 } | |
336 | |
337 /** | |
338 * Makes a request in another process. | |
339 * | |
340 * @param object $request An origin request instance | |
341 * | |
342 * @return object An origin response instance | |
343 * | |
344 * @throws \RuntimeException When processing returns exit code | |
345 */ | |
346 protected function doRequestInProcess($request) | |
347 { | |
348 $process = new PhpProcess($this->getScript($request), null, null); | |
349 $process->run(); | |
350 | |
351 if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) { | |
352 throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput())); | |
353 } | |
354 | |
355 return unserialize($process->getOutput()); | |
356 } | |
357 | |
358 /** | |
359 * Makes a request. | |
360 * | |
361 * @param object $request An origin request instance | |
362 * | |
363 * @return object An origin response instance | |
364 */ | |
365 abstract protected function doRequest($request); | |
366 | |
367 /** | |
368 * Returns the script to execute when the request must be insulated. | |
369 * | |
370 * @param object $request An origin request instance | |
371 * | |
372 * @throws \LogicException When this abstract class is not implemented | |
373 */ | |
374 protected function getScript($request) | |
375 { | |
376 throw new \LogicException('To insulate requests, you need to override the getScript() method.'); | |
377 } | |
378 | |
379 /** | |
380 * Filters the BrowserKit request to the origin one. | |
381 * | |
382 * @param Request $request The BrowserKit Request to filter | |
383 * | |
384 * @return object An origin request instance | |
385 */ | |
386 protected function filterRequest(Request $request) | |
387 { | |
388 return $request; | |
389 } | |
390 | |
391 /** | |
392 * Filters the origin response to the BrowserKit one. | |
393 * | |
394 * @param object $response The origin response to filter | |
395 * | |
396 * @return Response An BrowserKit Response instance | |
397 */ | |
398 protected function filterResponse($response) | |
399 { | |
400 return $response; | |
401 } | |
402 | |
403 /** | |
404 * Creates a crawler. | |
405 * | |
406 * This method returns null if the DomCrawler component is not available. | |
407 * | |
408 * @param string $uri A URI | |
409 * @param string $content Content for the crawler to use | |
410 * @param string $type Content type | |
411 * | |
412 * @return Crawler|null | |
413 */ | |
414 protected function createCrawlerFromContent($uri, $content, $type) | |
415 { | |
416 if (!class_exists('Symfony\Component\DomCrawler\Crawler')) { | |
417 return; | |
418 } | |
419 | |
420 $crawler = new Crawler(null, $uri); | |
421 $crawler->addContent($content, $type); | |
422 | |
423 return $crawler; | |
424 } | |
425 | |
426 /** | |
427 * Goes back in the browser history. | |
428 * | |
429 * @return Crawler | |
430 */ | |
431 public function back() | |
432 { | |
433 return $this->requestFromRequest($this->history->back(), false); | |
434 } | |
435 | |
436 /** | |
437 * Goes forward in the browser history. | |
438 * | |
439 * @return Crawler | |
440 */ | |
441 public function forward() | |
442 { | |
443 return $this->requestFromRequest($this->history->forward(), false); | |
444 } | |
445 | |
446 /** | |
447 * Reloads the current browser. | |
448 * | |
449 * @return Crawler | |
450 */ | |
451 public function reload() | |
452 { | |
453 return $this->requestFromRequest($this->history->current(), false); | |
454 } | |
455 | |
456 /** | |
457 * Follow redirects? | |
458 * | |
459 * @return Crawler | |
460 * | |
461 * @throws \LogicException If request was not a redirect | |
462 */ | |
463 public function followRedirect() | |
464 { | |
465 if (empty($this->redirect)) { | |
466 throw new \LogicException('The request was not redirected.'); | |
467 } | |
468 | |
469 if (-1 !== $this->maxRedirects) { | |
470 if ($this->redirectCount > $this->maxRedirects) { | |
471 throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects)); | |
472 } | |
473 } | |
474 | |
475 $request = $this->internalRequest; | |
476 | |
477 if (in_array($this->internalResponse->getStatus(), array(302, 303))) { | |
478 $method = 'GET'; | |
479 $files = array(); | |
480 $content = null; | |
481 } else { | |
482 $method = $request->getMethod(); | |
483 $files = $request->getFiles(); | |
484 $content = $request->getContent(); | |
485 } | |
486 | |
487 if ('GET' === strtoupper($method)) { | |
488 // Don't forward parameters for GET request as it should reach the redirection URI | |
489 $parameters = array(); | |
490 } else { | |
491 $parameters = $request->getParameters(); | |
492 } | |
493 | |
494 $server = $request->getServer(); | |
495 $server = $this->updateServerFromUri($server, $this->redirect); | |
496 | |
497 $this->isMainRequest = false; | |
498 | |
499 $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content); | |
500 | |
501 $this->isMainRequest = true; | |
502 | |
503 return $response; | |
504 } | |
505 | |
506 /** | |
507 * Restarts the client. | |
508 * | |
509 * It flushes history and all cookies. | |
510 */ | |
511 public function restart() | |
512 { | |
513 $this->cookieJar->clear(); | |
514 $this->history->clear(); | |
515 } | |
516 | |
517 /** | |
518 * Takes a URI and converts it to absolute if it is not already absolute. | |
519 * | |
520 * @param string $uri A URI | |
521 * | |
522 * @return string An absolute URI | |
523 */ | |
524 protected function getAbsoluteUri($uri) | |
525 { | |
526 // already absolute? | |
527 if (0 === strpos($uri, 'http://') || 0 === strpos($uri, 'https://')) { | |
528 return $uri; | |
529 } | |
530 | |
531 if (!$this->history->isEmpty()) { | |
532 $currentUri = $this->history->current()->getUri(); | |
533 } else { | |
534 $currentUri = sprintf('http%s://%s/', | |
535 isset($this->server['HTTPS']) ? 's' : '', | |
536 isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost' | |
537 ); | |
538 } | |
539 | |
540 // protocol relative URL | |
541 if (0 === strpos($uri, '//')) { | |
542 return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri; | |
543 } | |
544 | |
545 // anchor or query string parameters? | |
546 if (!$uri || '#' == $uri[0] || '?' == $uri[0]) { | |
547 return preg_replace('/[#?].*?$/', '', $currentUri).$uri; | |
548 } | |
549 | |
550 if ('/' !== $uri[0]) { | |
551 $path = parse_url($currentUri, PHP_URL_PATH); | |
552 | |
553 if ('/' !== substr($path, -1)) { | |
554 $path = substr($path, 0, strrpos($path, '/') + 1); | |
555 } | |
556 | |
557 $uri = $path.$uri; | |
558 } | |
559 | |
560 return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri; | |
561 } | |
562 | |
563 /** | |
564 * Makes a request from a Request object directly. | |
565 * | |
566 * @param Request $request A Request instance | |
567 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload()) | |
568 * | |
569 * @return Crawler | |
570 */ | |
571 protected function requestFromRequest(Request $request, $changeHistory = true) | |
572 { | |
573 return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory); | |
574 } | |
575 | |
576 private function updateServerFromUri($server, $uri) | |
577 { | |
578 $server['HTTP_HOST'] = $this->extractHost($uri); | |
579 $scheme = parse_url($uri, PHP_URL_SCHEME); | |
580 $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme; | |
581 unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']); | |
582 | |
583 return $server; | |
584 } | |
585 | |
586 private function extractHost($uri) | |
587 { | |
588 $host = parse_url($uri, PHP_URL_HOST); | |
589 | |
590 if ($port = parse_url($uri, PHP_URL_PORT)) { | |
591 return $host.':'.$port; | |
592 } | |
593 | |
594 return $host; | |
595 } | |
596 } |