PageRenderTime 24ms CodeModel.GetById 8ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/symfony/src/Symfony/Component/BrowserKit/Client.php

https://bitbucket.org/cryofrost/portal
PHP | 489 lines | 195 code | 54 blank | 240 comment | 21 complexity | fb20335e8f56db7478be7801587804db MD5 | raw file
Possible License(s): Apache-2.0, JSON, LGPL-2.1, LGPL-2.0, LGPL-3.0, BSD-3-Clause, BSD-2-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\BrowserKit;
  11. use Symfony\Component\DomCrawler\Crawler;
  12. use Symfony\Component\DomCrawler\Link;
  13. use Symfony\Component\DomCrawler\Form;
  14. use Symfony\Component\Process\PhpProcess;
  15. use Symfony\Component\BrowserKit\Request;
  16. use Symfony\Component\BrowserKit\Response;
  17. use Symfony\Component\BrowserKit\Client;
  18. /**
  19. * Client simulates a browser.
  20. *
  21. * To make the actual request, you need to implement the doRequest() method.
  22. *
  23. * If you want to be able to run requests in their own process (insulated flag),
  24. * you need to also implement the getScript() method.
  25. *
  26. * @author Fabien Potencier <fabien@symfony.com>
  27. *
  28. * @api
  29. */
  30. abstract class Client
  31. {
  32. protected $history;
  33. protected $cookieJar;
  34. protected $server;
  35. protected $request;
  36. protected $response;
  37. protected $crawler;
  38. protected $insulated;
  39. protected $redirect;
  40. protected $followRedirects;
  41. /**
  42. * Constructor.
  43. *
  44. * @param array $server The server parameters (equivalent of $_SERVER)
  45. * @param History $history A History instance to store the browser history
  46. * @param CookieJar $cookieJar A CookieJar instance to store the cookies
  47. *
  48. * @api
  49. */
  50. public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
  51. {
  52. $this->setServerParameters($server);
  53. $this->history = null === $history ? new History() : $history;
  54. $this->cookieJar = null === $cookieJar ? new CookieJar() : $cookieJar;
  55. $this->insulated = false;
  56. $this->followRedirects = true;
  57. }
  58. /**
  59. * Sets whether to automatically follow redirects or not.
  60. *
  61. * @param Boolean $followRedirect Whether to follow redirects
  62. *
  63. * @api
  64. */
  65. public function followRedirects($followRedirect = true)
  66. {
  67. $this->followRedirects = (Boolean) $followRedirect;
  68. }
  69. /**
  70. * Sets the insulated flag.
  71. *
  72. * @param Boolean $insulated Whether to insulate the requests or not
  73. *
  74. * @throws \RuntimeException When Symfony Process Component is not installed
  75. *
  76. * @api
  77. */
  78. public function insulate($insulated = true)
  79. {
  80. if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
  81. // @codeCoverageIgnoreStart
  82. throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
  83. // @codeCoverageIgnoreEnd
  84. }
  85. $this->insulated = (Boolean) $insulated;
  86. }
  87. /**
  88. * Sets server parameters.
  89. *
  90. * @param array $server An array of server parameters
  91. *
  92. * @api
  93. */
  94. public function setServerParameters(array $server)
  95. {
  96. $this->server = array_merge(array(
  97. 'HTTP_HOST' => 'localhost',
  98. 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
  99. ), $server);
  100. }
  101. /**
  102. * Sets single server parameter.
  103. *
  104. * @param string $key A key of the parameter
  105. * @param string $value A value of the parameter
  106. */
  107. public function setServerParameter($key, $value)
  108. {
  109. $this->server[$key] = $value;
  110. }
  111. /**
  112. * Gets single server parameter for specified key.
  113. *
  114. * @param string $key A key of the parameter to get
  115. * @param string $default A default value when key is undefined
  116. *
  117. * @return string A value of the parameter
  118. */
  119. public function getServerParameter($key, $default = '')
  120. {
  121. return (isset($this->server[$key])) ? $this->server[$key] : $default;
  122. }
  123. /**
  124. * Returns the History instance.
  125. *
  126. * @return History A History instance
  127. *
  128. * @api
  129. */
  130. public function getHistory()
  131. {
  132. return $this->history;
  133. }
  134. /**
  135. * Returns the CookieJar instance.
  136. *
  137. * @return CookieJar A CookieJar instance
  138. *
  139. * @api
  140. */
  141. public function getCookieJar()
  142. {
  143. return $this->cookieJar;
  144. }
  145. /**
  146. * Returns the current Crawler instance.
  147. *
  148. * @return Crawler A Crawler instance
  149. *
  150. * @api
  151. */
  152. public function getCrawler()
  153. {
  154. return $this->crawler;
  155. }
  156. /**
  157. * Returns the current Response instance.
  158. *
  159. * @return Response A Response instance
  160. *
  161. * @api
  162. */
  163. public function getResponse()
  164. {
  165. return $this->response;
  166. }
  167. /**
  168. * Returns the current Request instance.
  169. *
  170. * @return Request A Request instance
  171. *
  172. * @api
  173. */
  174. public function getRequest()
  175. {
  176. return $this->request;
  177. }
  178. /**
  179. * Clicks on a given link.
  180. *
  181. * @param Link $link A Link instance
  182. *
  183. * @return Crawler
  184. *
  185. * @api
  186. */
  187. public function click(Link $link)
  188. {
  189. if ($link instanceof Form) {
  190. return $this->submit($link);
  191. }
  192. return $this->request($link->getMethod(), $link->getUri());
  193. }
  194. /**
  195. * Submits a form.
  196. *
  197. * @param Form $form A Form instance
  198. * @param array $values An array of form field values
  199. *
  200. * @return Crawler
  201. *
  202. * @api
  203. */
  204. public function submit(Form $form, array $values = array())
  205. {
  206. $form->setValues($values);
  207. return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
  208. }
  209. /**
  210. * Calls a URI.
  211. *
  212. * @param string $method The request method
  213. * @param string $uri The URI to fetch
  214. * @param array $parameters The Request parameters
  215. * @param array $files The files
  216. * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
  217. * @param string $content The raw body data
  218. * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
  219. *
  220. * @return Crawler
  221. *
  222. * @api
  223. */
  224. public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
  225. {
  226. $uri = $this->getAbsoluteUri($uri);
  227. $server = array_merge($this->server, $server);
  228. if (!$this->history->isEmpty()) {
  229. $server['HTTP_REFERER'] = $this->history->current()->getUri();
  230. }
  231. $server['HTTP_HOST'] = parse_url($uri, PHP_URL_HOST);
  232. $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
  233. $request = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
  234. $this->request = $this->filterRequest($request);
  235. if (true === $changeHistory) {
  236. $this->history->add($request);
  237. }
  238. if ($this->insulated) {
  239. $this->response = $this->doRequestInProcess($this->request);
  240. } else {
  241. $this->response = $this->doRequest($this->request);
  242. }
  243. $response = $this->filterResponse($this->response);
  244. $this->cookieJar->updateFromResponse($response);
  245. $this->redirect = $response->getHeader('Location');
  246. if ($this->followRedirects && $this->redirect) {
  247. return $this->crawler = $this->followRedirect();
  248. }
  249. return $this->crawler = $this->createCrawlerFromContent($request->getUri(), $response->getContent(), $response->getHeader('Content-Type'));
  250. }
  251. /**
  252. * Makes a request in another process.
  253. *
  254. * @param Request $request A Request instance
  255. *
  256. * @return Response A Response instance
  257. *
  258. * @throws \RuntimeException When processing returns exit code
  259. */
  260. protected function doRequestInProcess($request)
  261. {
  262. // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user.
  263. $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir()));
  264. $process->run();
  265. if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
  266. throw new \RuntimeException($process->getErrorOutput());
  267. }
  268. return unserialize($process->getOutput());
  269. }
  270. /**
  271. * Makes a request.
  272. *
  273. * @param Request $request A Request instance
  274. *
  275. * @return Response A Response instance
  276. */
  277. abstract protected function doRequest($request);
  278. /**
  279. * Returns the script to execute when the request must be insulated.
  280. *
  281. * @param Request $request A Request instance
  282. *
  283. * @throws \LogicException When this abstract class is not implemented
  284. */
  285. protected function getScript($request)
  286. {
  287. // @codeCoverageIgnoreStart
  288. throw new \LogicException('To insulate requests, you need to override the getScript() method.');
  289. // @codeCoverageIgnoreEnd
  290. }
  291. /**
  292. * Filters the request.
  293. *
  294. * @param Request $request The request to filter
  295. *
  296. * @return Request
  297. */
  298. protected function filterRequest(Request $request)
  299. {
  300. return $request;
  301. }
  302. /**
  303. * Filters the Response.
  304. *
  305. * @param Response $response The Response to filter
  306. *
  307. * @return Response
  308. */
  309. protected function filterResponse($response)
  310. {
  311. return $response;
  312. }
  313. /**
  314. * Creates a crawler.
  315. *
  316. * @param string $uri A uri
  317. * @param string $content Content for the crawler to use
  318. * @param string $type Content type
  319. *
  320. * @return Crawler
  321. */
  322. protected function createCrawlerFromContent($uri, $content, $type)
  323. {
  324. $crawler = new Crawler(null, $uri);
  325. $crawler->addContent($content, $type);
  326. return $crawler;
  327. }
  328. /**
  329. * Goes back in the browser history.
  330. *
  331. * @return Crawler
  332. *
  333. * @api
  334. */
  335. public function back()
  336. {
  337. return $this->requestFromRequest($this->history->back(), false);
  338. }
  339. /**
  340. * Goes forward in the browser history.
  341. *
  342. * @return Crawler
  343. *
  344. * @api
  345. */
  346. public function forward()
  347. {
  348. return $this->requestFromRequest($this->history->forward(), false);
  349. }
  350. /**
  351. * Reloads the current browser.
  352. *
  353. * @return Crawler
  354. *
  355. * @api
  356. */
  357. public function reload()
  358. {
  359. return $this->requestFromRequest($this->history->current(), false);
  360. }
  361. /**
  362. * Follow redirects?
  363. *
  364. * @return Crawler
  365. *
  366. * @throws \LogicException If request was not a redirect
  367. *
  368. * @api
  369. */
  370. public function followRedirect()
  371. {
  372. if (empty($this->redirect)) {
  373. throw new \LogicException('The request was not redirected.');
  374. }
  375. return $this->request('get', $this->redirect);
  376. }
  377. /**
  378. * Restarts the client.
  379. *
  380. * It flushes history and all cookies.
  381. *
  382. * @api
  383. */
  384. public function restart()
  385. {
  386. $this->cookieJar->clear();
  387. $this->history->clear();
  388. }
  389. /**
  390. * Takes a URI and converts it to absolute if it is not already absolute.
  391. *
  392. * @param string $uri A uri
  393. *
  394. * @return string An absolute uri
  395. */
  396. protected function getAbsoluteUri($uri)
  397. {
  398. // already absolute?
  399. if (0 === strpos($uri, 'http')) {
  400. return $uri;
  401. }
  402. if (!$this->history->isEmpty()) {
  403. $currentUri = $this->history->current()->getUri();
  404. } else {
  405. $currentUri = sprintf('http%s://%s/',
  406. isset($this->server['HTTPS']) ? 's' : '',
  407. isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
  408. );
  409. }
  410. // anchor?
  411. if (!$uri || '#' == $uri[0]) {
  412. return preg_replace('/#.*?$/', '', $currentUri).$uri;
  413. }
  414. if ('/' !== $uri[0]) {
  415. $path = parse_url($currentUri, PHP_URL_PATH);
  416. if ('/' !== substr($path, -1)) {
  417. $path = substr($path, 0, strrpos($path, '/') + 1);
  418. }
  419. $uri = $path.$uri;
  420. }
  421. return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
  422. }
  423. /**
  424. * Makes a request from a Request object directly.
  425. *
  426. * @param Request $request A Request instance
  427. * @param Boolean $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
  428. *
  429. * @return Crawler
  430. */
  431. protected function requestFromRequest(Request $request, $changeHistory = true)
  432. {
  433. return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
  434. }
  435. }