PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/samples/PHP/Client.php

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