Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 620 lines (540 sloc) 17.111 kb
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
1 <?php
2
2d3bf28 @blue-eyes normalized license messages in PHP files
blue-eyes authored
3 /*
4 * This file is part of the Symfony package.
5 *
4953471 @fabpot replaced symfony-project.org by symfony.com
fabpot authored
6 * (c) Fabien Potencier <fabien@symfony.com>
2d3bf28 @blue-eyes normalized license messages in PHP files
blue-eyes authored
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
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 *
4953471 @fabpot replaced symfony-project.org by symfony.com
fabpot authored
27 * @author Fabien Potencier <fabien@symfony.com>
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
28 *
29 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
30 */
31 abstract class Client
32 {
33 protected $history;
34 protected $cookieJar;
9374817 @Tobion unify constructor initialization style throughout symfony
Tobion authored
35 protected $server = array();
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
36 protected $internalRequest;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
37 protected $request;
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
38 protected $internalResponse;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
39 protected $response;
40 protected $crawler;
9374817 @Tobion unify constructor initialization style throughout symfony
Tobion authored
41 protected $insulated = false;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
42 protected $redirect;
9374817 @Tobion unify constructor initialization style throughout symfony
Tobion authored
43 protected $followRedirects = true;
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
44
9374817 @Tobion unify constructor initialization style throughout symfony
Tobion authored
45 private $maxRedirects = -1;
46 private $redirectCount = 0;
47 private $isMainRequest = true;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
48
49 /**
50 * Constructor.
51 *
52 * @param array $server The server parameters (equivalent of $_SERVER)
53 * @param History $history A History instance to store the browser history
54 * @param CookieJar $cookieJar A CookieJar instance to store the cookies
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
55 *
56 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
57 */
58 public function __construct(array $server = array(), History $history = null, CookieJar $cookieJar = null)
59 {
60 $this->setServerParameters($server);
9374817 @Tobion unify constructor initialization style throughout symfony
Tobion authored
61 $this->history = $history ?: new History();
62 $this->cookieJar = $cookieJar ?: new CookieJar();
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
63 }
64
65 /**
66 * Sets whether to automatically follow redirects or not.
67 *
de4f2ed @GrahamCampbell Docblock fixes
GrahamCampbell authored
68 * @param bool $followRedirect Whether to follow redirects
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
69 *
70 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
71 */
72 public function followRedirects($followRedirect = true)
73 {
488c5ea @fabpot made types consistent with those defined in Hack
fabpot authored
74 $this->followRedirects = (bool) $followRedirect;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
75 }
9d8f14a @fabpot fixed CS
fabpot authored
76
af96f9f @stloyd Add max redirections limit
stloyd authored
77 /**
78 * Sets the maximum number of requests that crawler can follow.
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
79 *
de4f2ed @GrahamCampbell Docblock fixes
GrahamCampbell authored
80 * @param int $maxRedirects
af96f9f @stloyd Add max redirections limit
stloyd authored
81 */
82 public function setMaxRedirects($maxRedirects)
83 {
84 $this->maxRedirects = $maxRedirects < 0 ? -1 : $maxRedirects;
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
85 $this->followRedirects = -1 != $this->maxRedirects;
af96f9f @stloyd Add max redirections limit
stloyd authored
86 }
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
87
88 /**
89 * Sets the insulated flag.
90 *
de4f2ed @GrahamCampbell Docblock fixes
GrahamCampbell authored
91 * @param bool $insulated Whether to insulate the requests or not
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
92 *
93 * @throws \RuntimeException When Symfony Process Component is not installed
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
94 *
95 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
96 */
97 public function insulate($insulated = true)
98 {
f154f6d @GromNaN [BrowserKit] Check class existence only when required.
GromNaN authored
99 if ($insulated && !class_exists('Symfony\\Component\\Process\\Process')) {
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
100 throw new \RuntimeException('Unable to isolate requests as the Symfony Process Component is not installed.');
101 }
102
488c5ea @fabpot made types consistent with those defined in Hack
fabpot authored
103 $this->insulated = (bool) $insulated;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
104 }
105
106 /**
107 * Sets server parameters.
108 *
109 * @param array $server An array of server parameters
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
110 *
111 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
112 */
113 public function setServerParameters(array $server)
114 {
115 $this->server = array_merge(array(
a8e5c48 @disquedur Remove aligned '=>' and '='
disquedur authored
116 'HTTP_HOST' => 'localhost',
395a37f @sasezaki Don't tell a lie to every WebServers
sasezaki authored
117 'HTTP_USER_AGENT' => 'Symfony2 BrowserKit',
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
118 ), $server);
119 }
120
121 /**
a55f741 @hidenorigoto [BrowserKit] added the method to Client which enables to set single serv...
hidenorigoto authored
122 * Sets single server parameter.
123 *
124 * @param string $key A key of the parameter
125 * @param string $value A value of the parameter
126 */
127 public function setServerParameter($key, $value)
128 {
129 $this->server[$key] = $value;
130 }
131
132 /**
3c57789 @hidenorigoto [BrowserKit] added getServerParameter method which makes setServerParame...
hidenorigoto authored
133 * Gets single server parameter for specified key.
134 *
135 * @param string $key A key of the parameter to get
136 * @param string $default A default value when key is undefined
28a0e7e @fabpot [DoctrineBridge] fixed some CS
fabpot authored
137 *
3c57789 @hidenorigoto [BrowserKit] added getServerParameter method which makes setServerParame...
hidenorigoto authored
138 * @return string A value of the parameter
139 */
140 public function getServerParameter($key, $default = '')
141 {
142 return (isset($this->server[$key])) ? $this->server[$key] : $default;
143 }
144
145 /**
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
146 * Returns the History instance.
147 *
148 * @return History A History instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
149 *
150 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
151 */
152 public function getHistory()
153 {
154 return $this->history;
155 }
156
157 /**
158 * Returns the CookieJar instance.
159 *
160 * @return CookieJar A CookieJar instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
161 *
162 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
163 */
164 public function getCookieJar()
165 {
166 return $this->cookieJar;
167 }
168
169 /**
170 * Returns the current Crawler instance.
171 *
df0c76d @fabpot made some small tweaks
fabpot authored
172 * @return Crawler|null A Crawler instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
173 *
174 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
175 */
176 public function getCrawler()
177 {
178 return $this->crawler;
179 }
180
181 /**
df0c76d @fabpot made some small tweaks
fabpot authored
182 * Returns the current BrowserKit Response instance.
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
183 *
df0c76d @fabpot made some small tweaks
fabpot authored
184 * @return Response|null A BrowserKit Response instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
185 *
186 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
187 */
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
188 public function getInternalResponse()
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
189 {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
190 return $this->internalResponse;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
191 }
192
193 /**
df0c76d @fabpot made some small tweaks
fabpot authored
194 * Returns the current origin response instance.
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
195 *
196 * The origin response is the response instance that is returned
197 * by the code that handles requests.
198 *
df0c76d @fabpot made some small tweaks
fabpot authored
199 * @return object|null A response instance
200 *
e454a28 @hhamon Fixes various phpdoc and coding standards.
hhamon authored
201 * @see doRequest()
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
202 *
203 * @api
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
204 */
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
205 public function getResponse()
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
206 {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
207 return $this->response;
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
208 }
209
210 /**
df0c76d @fabpot made some small tweaks
fabpot authored
211 * Returns the current BrowserKit Request instance.
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
212 *
df0c76d @fabpot made some small tweaks
fabpot authored
213 * @return Request|null A BrowserKit Request instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
214 *
215 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
216 */
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
217 public function getInternalRequest()
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
218 {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
219 return $this->internalRequest;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
220 }
221
222 /**
df0c76d @fabpot made some small tweaks
fabpot authored
223 * Returns the current origin Request instance.
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
224 *
225 * The origin request is the request instance that is sent
226 * to the code that handles requests.
227 *
df0c76d @fabpot made some small tweaks
fabpot authored
228 * @return object|null A Request instance
229 *
e454a28 @hhamon Fixes various phpdoc and coding standards.
hhamon authored
230 * @see doRequest()
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
231 *
df0c76d @fabpot made some small tweaks
fabpot authored
232 * @api
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
233 */
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
234 public function getRequest()
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
235 {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
236 return $this->request;
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
237 }
238
239 /**
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
240 * Clicks on a given link.
241 *
242 * @param Link $link A Link instance
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
243 *
cf25af6 @jcrombez [BrowserKit] Missing @return Crawler annotation for the click() Client m...
jcrombez authored
244 * @return Crawler
245 *
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
246 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
247 */
248 public function click(Link $link)
249 {
66eadd4 @fabpot [BrowserKit] convert a click to a submit when the link is actually a for...
fabpot authored
250 if ($link instanceof Form) {
251 return $this->submit($link);
252 }
253
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
254 return $this->request($link->getMethod(), $link->getUri());
255 }
256
257 /**
258 * Submits a form.
259 *
260 * @param Form $form A Form instance
261 * @param array $values An array of form field values
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
262 *
d3ad106 @hhamon [BrowserKit] added missing @return PHPDoc for the Client::submit() metho...
hhamon authored
263 * @return Crawler
264 *
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
265 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
266 */
267 public function submit(Form $form, array $values = array())
268 {
269 $form->setValues($values);
270
a9f11e6 @ornicar [BrowserKit] Fix parameters order in Client::submit
ornicar authored
271 return $this->request($form->getMethod(), $form->getUri(), $form->getPhpValues(), $form->getPhpFiles());
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
272 }
273
274 /**
275 * Calls a URI.
276 *
de4f2ed @GrahamCampbell Docblock fixes
GrahamCampbell authored
277 * @param string $method The request method
278 * @param string $uri The URI to fetch
279 * @param array $parameters The Request parameters
280 * @param array $files The files
281 * @param array $server The server parameters (HTTP headers are referenced with a HTTP_ prefix as PHP does)
282 * @param string $content The raw body data
283 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
284 *
285 * @return Crawler
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
286 *
287 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
288 */
1b4a0a9 @martijn4evers [Testing][HttpKernel] Added possibility to functional test raw body data
martijn4evers authored
289 public function request($method, $uri, array $parameters = array(), array $files = array(), array $server = array(), $content = null, $changeHistory = true)
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
290 {
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
291 if ($this->isMainRequest) {
292 $this->redirectCount = 0;
293 } else {
294 ++$this->redirectCount;
295 }
296
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
297 $uri = $this->getAbsoluteUri($uri);
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
298
262d698 @pkruithof Fixed relative redirects for ambiguous paths
pkruithof authored
299 if (!empty($server['HTTP_HOST'])) {
46b0066 @fabpot [BrowserKit] refactor code and fix unquoted regex
fabpot authored
300 $uri = preg_replace('{^(https?\://)'.preg_quote($this->extractHost($uri)).'}', '${1}'.$server['HTTP_HOST'], $uri);
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
301 }
302
303 if (isset($server['HTTPS'])) {
df9a4c0 @fabpot fixed too greedy replacements
fabpot authored
304 $uri = preg_replace('{^'.parse_url($uri, PHP_URL_SCHEME).'}', $server['HTTPS'] ? 'https' : 'http', $uri);
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
305 }
306
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
307 $server = array_merge($this->server, $server);
2d47a87 @kbond add non-standard port to HTTP_HOST
kbond authored
308
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
309 if (!$this->history->isEmpty()) {
310 $server['HTTP_REFERER'] = $this->history->current()->getUri();
311 }
2d47a87 @kbond add non-standard port to HTTP_HOST
kbond authored
312
46b0066 @fabpot [BrowserKit] refactor code and fix unquoted regex
fabpot authored
313 $server['HTTP_HOST'] = $this->extractHost($uri);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
314 $server['HTTPS'] = 'https' == parse_url($uri, PHP_URL_SCHEME);
315
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
316 $this->internalRequest = new Request($uri, $method, $parameters, $files, $this->cookieJar->allValues($uri), $server, $content);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
317
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
318 $this->request = $this->filterRequest($this->internalRequest);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
319
320 if (true === $changeHistory) {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
321 $this->history->add($this->internalRequest);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
322 }
323
324 if ($this->insulated) {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
325 $this->response = $this->doRequestInProcess($this->request);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
326 } else {
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
327 $this->response = $this->doRequest($this->request);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
328 }
329
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
330 $this->internalResponse = $this->filterResponse($this->response);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
331
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
332 $this->cookieJar->updateFromResponse($this->internalResponse, $uri);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
333
9074683 [BrowserKit] should not follow redirects if status code is not 30x
m.chwedziak authored
334 $status = $this->internalResponse->getStatus();
9d8f14a @fabpot fixed CS
fabpot authored
335
9074683 [BrowserKit] should not follow redirects if status code is not 30x
m.chwedziak authored
336 if ($status >= 300 && $status < 400) {
337 $this->redirect = $this->internalResponse->getHeader('Location');
338 } else {
339 $this->redirect = null;
340 }
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
341
342 if ($this->followRedirects && $this->redirect) {
343 return $this->crawler = $this->followRedirect();
344 }
345
7ca7d73 @fabpot alternate fix where we had accessor for the BrowerKit request/response i...
fabpot authored
346 return $this->crawler = $this->createCrawlerFromContent($this->internalRequest->getUri(), $this->internalResponse->getContent(), $this->internalResponse->getHeader('Content-Type'));
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
347 }
348
349 /**
350 * Makes a request in another process.
351 *
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
352 * @param object $request An origin request instance
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
353 *
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
354 * @return object An origin response instance
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
355 *
356 * @throws \RuntimeException When processing returns exit code
357 */
358 protected function doRequestInProcess($request)
359 {
ecd31b6 @aboks [BrowserKit] Fixed incorrect temp dir when running insulated functional ...
aboks authored
360 // We set the TMPDIR (for Macs) and TEMP (for Windows), because on these platforms the temp directory changes based on the user.
361 $process = new PhpProcess($this->getScript($request), null, array('TMPDIR' => sys_get_temp_dir(), 'TEMP' => sys_get_temp_dir()));
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
362 $process->run();
363
71990a3 @fabpot [BrowserKit] fixed a weird behavior where a PHP process returns a succes...
fabpot authored
364 if (!$process->isSuccessful() || !preg_match('/^O\:\d+\:/', $process->getOutput())) {
08f303d @dantleech Enforce sprintf for exceptions
dantleech authored
365 throw new \RuntimeException(sprintf('OUTPUT: %s ERROR OUTPUT: %s', $process->getOutput(), $process->getErrorOutput()));
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
366 }
367
368 return unserialize($process->getOutput());
369 }
370
371 /**
372 * Makes a request.
373 *
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
374 * @param object $request An origin request instance
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
375 *
482bbd9 @fabpot fixed Client implementation to return the right Response (closes #4475)
fabpot authored
376 * @return object An origin response instance
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
377 */
378 abstract protected function doRequest($request);
379
380 /**
381 * Returns the script to execute when the request must be insulated.
382 *
df0c76d @fabpot made some small tweaks
fabpot authored
383 * @param object $request An origin request instance
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
384 *
385 * @throws \LogicException When this abstract class is not implemented
386 */
387 protected function getScript($request)
388 {
389 throw new \LogicException('To insulate requests, you need to override the getScript() method.');
390 }
391
40db1cd @merk Added phpdoc to BrowserKit
merk authored
392 /**
df0c76d @fabpot made some small tweaks
fabpot authored
393 * Filters the BrowserKit request to the origin one.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
394 *
df0c76d @fabpot made some small tweaks
fabpot authored
395 * @param Request $request The BrowserKit Request to filter
40db1cd @merk Added phpdoc to BrowserKit
merk authored
396 *
df0c76d @fabpot made some small tweaks
fabpot authored
397 * @return object An origin request instance
40db1cd @merk Added phpdoc to BrowserKit
merk authored
398 */
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
399 protected function filterRequest(Request $request)
400 {
401 return $request;
402 }
403
40db1cd @merk Added phpdoc to BrowserKit
merk authored
404 /**
df0c76d @fabpot made some small tweaks
fabpot authored
405 * Filters the origin response to the BrowserKit one.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
406 *
df0c76d @fabpot made some small tweaks
fabpot authored
407 * @param object $response The origin response to filter
40db1cd @merk Added phpdoc to BrowserKit
merk authored
408 *
df0c76d @fabpot made some small tweaks
fabpot authored
409 * @return Response An BrowserKit Response instance
40db1cd @merk Added phpdoc to BrowserKit
merk authored
410 */
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
411 protected function filterResponse($response)
412 {
413 return $response;
414 }
415
40db1cd @merk Added phpdoc to BrowserKit
merk authored
416 /**
070d54d @merk PHPDoc style fix
merk authored
417 * Creates a crawler.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
418 *
9460a0d @fabpot moved component and bridge unit tests to the src/ directory
fabpot authored
419 * This method returns null if the DomCrawler component is not available.
420 *
5b9f09b @fabpot fixed acronyms
fabpot authored
421 * @param string $uri A URI
0b5d347 @merk PHPDoc fixes
merk authored
422 * @param string $content Content for the crawler to use
28a0e7e @fabpot [DoctrineBridge] fixed some CS
fabpot authored
423 * @param string $type Content type
40db1cd @merk Added phpdoc to BrowserKit
merk authored
424 *
9460a0d @fabpot moved component and bridge unit tests to the src/ directory
fabpot authored
425 * @return Crawler|null
40db1cd @merk Added phpdoc to BrowserKit
merk authored
426 */
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
427 protected function createCrawlerFromContent($uri, $content, $type)
428 {
9460a0d @fabpot moved component and bridge unit tests to the src/ directory
fabpot authored
429 if (!class_exists('Symfony\Component\DomCrawler\Crawler')) {
9175571 @fabpot unified return null usages
fabpot authored
430 return;
9460a0d @fabpot moved component and bridge unit tests to the src/ directory
fabpot authored
431 }
432
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
433 $crawler = new Crawler(null, $uri);
434 $crawler->addContent($content, $type);
435
436 return $crawler;
437 }
438
439 /**
440 * Goes back in the browser history.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
441 *
442 * @return Crawler
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
443 *
444 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
445 */
446 public function back()
447 {
448 return $this->requestFromRequest($this->history->back(), false);
449 }
450
451 /**
452 * Goes forward in the browser history.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
453 *
454 * @return Crawler
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
455 *
456 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
457 */
458 public function forward()
459 {
460 return $this->requestFromRequest($this->history->forward(), false);
461 }
462
463 /**
464 * Reloads the current browser.
40db1cd @merk Added phpdoc to BrowserKit
merk authored
465 *
466 * @return Crawler
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
467 *
468 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
469 */
470 public function reload()
471 {
472 return $this->requestFromRequest($this->history->current(), false);
473 }
474
475 /**
476 * Follow redirects?
477 *
abf4cd2 @hhamon [BrowserKit] fix phpdoc for Client::followRedirect()
hhamon authored
478 * @return Crawler
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
479 *
480 * @throws \LogicException If request was not a redirect
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
481 *
482 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
483 */
484 public function followRedirect()
485 {
486 if (empty($this->redirect)) {
487 throw new \LogicException('The request was not redirected.');
488 }
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
489
af96f9f @stloyd Add max redirections limit
stloyd authored
490 if (-1 !== $this->maxRedirects) {
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
491 if ($this->redirectCount > $this->maxRedirects) {
af96f9f @stloyd Add max redirections limit
stloyd authored
492 throw new \LogicException(sprintf('The maximum number (%d) of redirections was reached.', $this->maxRedirects));
493 }
494 }
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
495
5cec136 @fabpot [BrowserKit] fixed method/files/content when redirecting a request
fabpot authored
496 $request = $this->internalRequest;
497
498 if (in_array($this->internalResponse->getStatus(), array(302, 303))) {
499 $method = 'get';
500 $files = array();
501 $content = null;
502 } else {
503 $method = $request->getMethod();
504 $files = $request->getFiles();
505 $content = $request->getContent();
506 }
507
a98e9f2 @stof [BrowserKit] Fixed the handling of parameters when redirecting
stof authored
508 if ('get' === strtolower($method)) {
509 // Don't forward parameters for GET request as it should reach the redirection URI
510 $parameters = array();
511 } else {
512 $parameters = $request->getParameters();
513 }
514
5cec136 @fabpot [BrowserKit] fixed method/files/content when redirecting a request
fabpot authored
515 $server = $request->getServer();
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
516 $server = $this->updateServerFromUri($server, $this->redirect);
b430aa5 @fabpot [BrowserKit] fixed headers when redirecting if history is set to false (...
fabpot authored
517
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
518 $this->isMainRequest = false;
519
be368b7 @fabpot Merge branch '2.2' into 2.3
fabpot authored
520 $response = $this->request($method, $this->redirect, $parameters, $files, $server, $content);
698d6aa @fabpot [BrowserKit] isolated the max redirect count to a given main request (re...
fabpot authored
521
522 $this->isMainRequest = true;
523
524 return $response;
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
525 }
526
527 /**
528 * Restarts the client.
529 *
4b1f133 @kwiateusz Edited src/Symfony/Component/BrowserKit/Client.php via GitHub
kwiateusz authored
530 * It flushes history and all cookies.
9839e64 @fabpot [BrowserKit] tagged the guaranteed BC API
fabpot authored
531 *
532 * @api
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
533 */
534 public function restart()
535 {
536 $this->cookieJar->clear();
537 $this->history->clear();
538 }
539
40db1cd @merk Added phpdoc to BrowserKit
merk authored
540 /**
541 * Takes a URI and converts it to absolute if it is not already absolute.
542 *
5b9f09b @fabpot fixed acronyms
fabpot authored
543 * @param string $uri A URI
28a0e7e @fabpot [DoctrineBridge] fixed some CS
fabpot authored
544 *
5b9f09b @fabpot fixed acronyms
fabpot authored
545 * @return string An absolute URI
40db1cd @merk Added phpdoc to BrowserKit
merk authored
546 */
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
547 protected function getAbsoluteUri($uri)
548 {
549 // already absolute?
cb427d4 @kriswallsmith optimized string starts with checks
kriswallsmith authored
550 if (0 === strpos($uri, 'http')) {
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
551 return $uri;
552 }
553
554 if (!$this->history->isEmpty()) {
555 $currentUri = $this->history->current()->getUri();
556 } else {
557 $currentUri = sprintf('http%s://%s/',
558 isset($this->server['HTTPS']) ? 's' : '',
559 isset($this->server['HTTP_HOST']) ? $this->server['HTTP_HOST'] : 'localhost'
560 );
561 }
562
bc7f635 @jgough bug #9445 [BrowserKit] fixed protocol-relative url redirection
jgough authored
563 // protocol relative URL
564 if (0 === strpos($uri, '//')) {
565 return parse_url($currentUri, PHP_URL_SCHEME).':'.$uri;
566 }
567
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
568 // anchor?
569 if (!$uri || '#' == $uri[0]) {
570 return preg_replace('/#.*?$/', '', $currentUri).$uri;
571 }
572
573 if ('/' !== $uri[0]) {
574 $path = parse_url($currentUri, PHP_URL_PATH);
575
576 if ('/' !== substr($path, -1)) {
577 $path = substr($path, 0, strrpos($path, '/') + 1);
578 }
579
580 $uri = $path.$uri;
581 }
582
583 return preg_replace('#^(.*?//[^/]+)\/.*$#', '$1', $currentUri).$uri;
584 }
585
586 /**
587 * Makes a request from a Request object directly.
588 *
589 * @param Request $request A Request instance
4df781e @fabpot made phpdoc types consistent with those defined in Hack
fabpot authored
590 * @param bool $changeHistory Whether to update the history or not (only used internally for back(), forward(), and reload())
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
591 *
592 * @return Crawler
593 */
594 protected function requestFromRequest(Request $request, $changeHistory = true)
595 {
091584f @clemherreman [BrowserKit] Fixed Client->back/forward/reload() not keeping all request...
clemherreman authored
596 return $this->request($request->getMethod(), $request->getUri(), $request->getParameters(), $request->getFiles(), $request->getServer(), $request->getContent(), $changeHistory);
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
597 }
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
598
599 private function updateServerFromUri($server, $uri)
600 {
d3588ea [BrowserKit] Fix browser kit redirect with ports
Walther Lalk authored
601 $server['HTTP_HOST'] = $this->extractHost($uri);
d744878 @fabpot fixed protocol-relative URLs
fabpot authored
602 $scheme = parse_url($uri, PHP_URL_SCHEME);
603 $server['HTTPS'] = null === $scheme ? $server['HTTPS'] : 'https' == $scheme;
2a14639 @cordoval added override power to server parameters provided on request method
cordoval authored
604 unset($server['HTTP_IF_NONE_MATCH'], $server['HTTP_IF_MODIFIED_SINCE']);
605
606 return $server;
607 }
46b0066 @fabpot [BrowserKit] refactor code and fix unquoted regex
fabpot authored
608
609 private function extractHost($uri)
610 {
611 $host = parse_url($uri, PHP_URL_HOST);
612
613 if ($port = parse_url($uri, PHP_URL_PORT)) {
614 return $host.':'.$port;
615 }
616
617 return $host;
618 }
72d921b @fabpot renamed Symfony\Components to Symfony\Component
fabpot authored
619 }
Something went wrong with that request. Please try again.