| [ Index ] |
PHP Cross Reference of Limb3 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Base include file for SimpleTest 4 * @package SimpleTest 5 * @subpackage WebTester 6 * @version $Id: browser.php 5999 2007-06-18 13:13:08Z pachanga $ 7 */ 8 9 /**#@+ 10 * include other SimpleTest class files 11 */ 12 require_once(dirname(__FILE__) . '/simpletest.php'); 13 require_once(dirname(__FILE__) . '/http.php'); 14 require_once(dirname(__FILE__) . '/encoding.php'); 15 require_once(dirname(__FILE__) . '/page.php'); 16 require_once(dirname(__FILE__) . '/selector.php'); 17 require_once(dirname(__FILE__) . '/frames.php'); 18 require_once(dirname(__FILE__) . '/user_agent.php'); 19 /**#@-*/ 20 21 if (!defined('DEFAULT_MAX_NESTED_FRAMES')) { 22 define('DEFAULT_MAX_NESTED_FRAMES', 3); 23 } 24 25 /** 26 * Browser history list. 27 * @package SimpleTest 28 * @subpackage WebTester 29 */ 30 class SimpleBrowserHistory { 31 var $_sequence; 32 var $_position; 33 34 /** 35 * Starts empty. 36 * @access public 37 */ 38 function SimpleBrowserHistory() { 39 $this->_sequence = array(); 40 $this->_position = -1; 41 } 42 43 /** 44 * Test for no entries yet. 45 * @return boolean True if empty. 46 * @access private 47 */ 48 function _isEmpty() { 49 return ($this->_position == -1); 50 } 51 52 /** 53 * Test for being at the beginning. 54 * @return boolean True if first. 55 * @access private 56 */ 57 function _atBeginning() { 58 return ($this->_position == 0) && ! $this->_isEmpty(); 59 } 60 61 /** 62 * Test for being at the last entry. 63 * @return boolean True if last. 64 * @access private 65 */ 66 function _atEnd() { 67 return ($this->_position + 1 >= count($this->_sequence)) && ! $this->_isEmpty(); 68 } 69 70 /** 71 * Adds a successfully fetched page to the history. 72 * @param SimpleUrl $url URL of fetch. 73 * @param SimpleEncoding $parameters Any post data with the fetch. 74 * @access public 75 */ 76 function recordEntry($url, $parameters) { 77 $this->_dropFuture(); 78 array_push( 79 $this->_sequence, 80 array('url' => $url, 'parameters' => $parameters)); 81 $this->_position++; 82 } 83 84 /** 85 * Last fully qualified URL for current history 86 * position. 87 * @return SimpleUrl URL for this position. 88 * @access public 89 */ 90 function getUrl() { 91 if ($this->_isEmpty()) { 92 return false; 93 } 94 return $this->_sequence[$this->_position]['url']; 95 } 96 97 /** 98 * Parameters of last fetch from current history 99 * position. 100 * @return SimpleFormEncoding Post parameters. 101 * @access public 102 */ 103 function getParameters() { 104 if ($this->_isEmpty()) { 105 return false; 106 } 107 return $this->_sequence[$this->_position]['parameters']; 108 } 109 110 /** 111 * Step back one place in the history. Stops at 112 * the first page. 113 * @return boolean True if any previous entries. 114 * @access public 115 */ 116 function back() { 117 if ($this->_isEmpty() || $this->_atBeginning()) { 118 return false; 119 } 120 $this->_position--; 121 return true; 122 } 123 124 /** 125 * Step forward one place. If already at the 126 * latest entry then nothing will happen. 127 * @return boolean True if any future entries. 128 * @access public 129 */ 130 function forward() { 131 if ($this->_isEmpty() || $this->_atEnd()) { 132 return false; 133 } 134 $this->_position++; 135 return true; 136 } 137 138 /** 139 * Ditches all future entries beyond the current 140 * point. 141 * @access private 142 */ 143 function _dropFuture() { 144 if ($this->_isEmpty()) { 145 return; 146 } 147 while (! $this->_atEnd()) { 148 array_pop($this->_sequence); 149 } 150 } 151 } 152 153 /** 154 * Simulated web browser. This is an aggregate of 155 * the user agent, the HTML parsing, request history 156 * and the last header set. 157 * @package SimpleTest 158 * @subpackage WebTester 159 */ 160 class SimpleBrowser { 161 var $_user_agent; 162 var $_page; 163 var $_history; 164 var $_ignore_frames; 165 var $_maximum_nested_frames; 166 167 /** 168 * Starts with a fresh browser with no 169 * cookie or any other state information. The 170 * exception is that a default proxy will be 171 * set up if specified in the options. 172 * @access public 173 */ 174 function SimpleBrowser() { 175 $this->_user_agent = &$this->_createUserAgent(); 176 $this->_user_agent->useProxy( 177 SimpleTest::getDefaultProxy(), 178 SimpleTest::getDefaultProxyUsername(), 179 SimpleTest::getDefaultProxyPassword()); 180 $this->_page = &new SimplePage(); 181 $this->_history = &$this->_createHistory(); 182 $this->_ignore_frames = false; 183 $this->_maximum_nested_frames = DEFAULT_MAX_NESTED_FRAMES; 184 } 185 186 /** 187 * Creates the underlying user agent. 188 * @return SimpleFetcher Content fetcher. 189 * @access protected 190 */ 191 function &_createUserAgent() { 192 $user_agent = &new SimpleUserAgent(); 193 return $user_agent; 194 } 195 196 /** 197 * Creates a new empty history list. 198 * @return SimpleBrowserHistory New list. 199 * @access protected 200 */ 201 function &_createHistory() { 202 $history = &new SimpleBrowserHistory(); 203 return $history; 204 } 205 206 /** 207 * Disables frames support. Frames will not be fetched 208 * and the frameset page will be used instead. 209 * @access public 210 */ 211 function ignoreFrames() { 212 $this->_ignore_frames = true; 213 } 214 215 /** 216 * Enables frames support. Frames will be fetched from 217 * now on. 218 * @access public 219 */ 220 function useFrames() { 221 $this->_ignore_frames = false; 222 } 223 224 /** 225 * Switches off cookie sending and recieving. 226 * @access public 227 */ 228 function ignoreCookies() { 229 $this->_user_agent->ignoreCookies(); 230 } 231 232 /** 233 * Switches back on the cookie sending and recieving. 234 * @access public 235 */ 236 function useCookies() { 237 $this->_user_agent->useCookies(); 238 } 239 240 /** 241 * Parses the raw content into a page. Will load further 242 * frame pages unless frames are disabled. 243 * @param SimpleHttpResponse $response Response from fetch. 244 * @param integer $depth Nested frameset depth. 245 * @return SimplePage Parsed HTML. 246 * @access private 247 */ 248 function &_parse($response, $depth = 0) { 249 $page = &$this->_buildPage($response); 250 if ($this->_ignore_frames || ! $page->hasFrames() || ($depth > $this->_maximum_nested_frames)) { 251 return $page; 252 } 253 $frameset = &new SimpleFrameset($page); 254 foreach ($page->getFrameset() as $key => $url) { 255 $frame = &$this->_fetch($url, new SimpleGetEncoding(), $depth + 1); 256 $frameset->addFrame($frame, $key); 257 } 258 return $frameset; 259 } 260 261 /** 262 * Assembles the parsing machinery and actually parses 263 * a single page. Frees all of the builder memory and so 264 * unjams the PHP memory management. 265 * @param SimpleHttpResponse $response Response from fetch. 266 * @return SimplePage Parsed top level page. 267 * @access protected 268 */ 269 function &_buildPage($response) { 270 $builder = &new SimplePageBuilder(); 271 $page = &$builder->parse($response); 272 $builder->free(); 273 unset($builder); 274 return $page; 275 } 276 277 /** 278 * Fetches a page. Jointly recursive with the _parse() 279 * method as it descends a frameset. 280 * @param string/SimpleUrl $url Target to fetch. 281 * @param SimpleEncoding $encoding GET/POST parameters. 282 * @param integer $depth Nested frameset depth protection. 283 * @return SimplePage Parsed page. 284 * @access private 285 */ 286 function &_fetch($url, $encoding, $depth = 0) { 287 $response = &$this->_user_agent->fetchResponse($url, $encoding); 288 if ($response->isError()) { 289 $page = &new SimplePage($response); 290 } else { 291 $page = &$this->_parse($response, $depth); 292 } 293 return $page; 294 } 295 296 /** 297 * Fetches a page or a single frame if that is the current 298 * focus. 299 * @param SimpleUrl $url Target to fetch. 300 * @param SimpleEncoding $parameters GET/POST parameters. 301 * @return string Raw content of page. 302 * @access private 303 */ 304 function _load($url, $parameters) { 305 $frame = $url->getTarget(); 306 if (! $frame || ! $this->_page->hasFrames() || (strtolower($frame) == '_top')) { 307 return $this->_loadPage($url, $parameters); 308 } 309 return $this->_loadFrame(array($frame), $url, $parameters); 310 } 311 312 /** 313 * Fetches a page and makes it the current page/frame. 314 * @param string/SimpleUrl $url Target to fetch as string. 315 * @param SimplePostEncoding $parameters POST parameters. 316 * @return string Raw content of page. 317 * @access private 318 */ 319 function _loadPage($url, $parameters) { 320 $this->_page = &$this->_fetch($url, $parameters); 321 $this->_history->recordEntry( 322 $this->_page->getUrl(), 323 $this->_page->getRequestData()); 324 return $this->_page->getRaw(); 325 } 326 327 /** 328 * Fetches a frame into the existing frameset replacing the 329 * original. 330 * @param array $frames List of names to drill down. 331 * @param string/SimpleUrl $url Target to fetch as string. 332 * @param SimpleFormEncoding $parameters POST parameters. 333 * @return string Raw content of page. 334 * @access private 335 */ 336 function _loadFrame($frames, $url, $parameters) { 337 $page = &$this->_fetch($url, $parameters); 338 $this->_page->setFrame($frames, $page); 339 } 340 341 /** 342 * Removes expired and temporary cookies as if 343 * the browser was closed and re-opened. 344 * @param string/integer $date Time when session restarted. 345 * If omitted then all persistent 346 * cookies are kept. 347 * @access public 348 */ 349 function restart($date = false) { 350 $this->_user_agent->restart($date); 351 } 352 353 /** 354 * Adds a header to every fetch. 355 * @param string $header Header line to add to every 356 * request until cleared. 357 * @access public 358 */ 359 function addHeader($header) { 360 $this->_user_agent->addHeader($header); 361 } 362 363 /** 364 * Ages the cookies by the specified time. 365 * @param integer $interval Amount in seconds. 366 * @access public 367 */ 368 function ageCookies($interval) { 369 $this->_user_agent->ageCookies($interval); 370 } 371 372 /** 373 * Sets an additional cookie. If a cookie has 374 * the same name and path it is replaced. 375 * @param string $name Cookie key. 376 * @param string $value Value of cookie. 377 * @param string $host Host upon which the cookie is valid. 378 * @param string $path Cookie path if not host wide. 379 * @param string $expiry Expiry date. 380 * @access public 381 */ 382 function setCookie($name, $value, $host = false, $path = '/', $expiry = false) { 383 $this->_user_agent->setCookie($name, $value, $host, $path, $expiry); 384 } 385 386 /** 387 * Reads the most specific cookie value from the 388 * browser cookies. 389 * @param string $host Host to search. 390 * @param string $path Applicable path. 391 * @param string $name Name of cookie to read. 392 * @return string False if not present, else the 393 * value as a string. 394 * @access public 395 */ 396 function getCookieValue($host, $path, $name) { 397 return $this->_user_agent->getCookieValue($host, $path, $name); 398 } 399 400 /** 401 * Reads the current cookies for the current URL. 402 * @param string $name Key of cookie to find. 403 * @return string Null if there is no current URL, false 404 * if the cookie is not set. 405 * @access public 406 */ 407 function getCurrentCookieValue($name) { 408 return $this->_user_agent->getBaseCookieValue($name, $this->_page->getUrl()); 409 } 410 411 /** 412 * Sets the maximum number of redirects before 413 * a page will be loaded anyway. 414 * @param integer $max Most hops allowed. 415 * @access public 416 */ 417 function setMaximumRedirects($max) { 418 $this->_user_agent->setMaximumRedirects($max); 419 } 420 421 /** 422 * Sets the maximum number of nesting of framed pages 423 * within a framed page to prevent loops. 424 * @param integer $max Highest depth allowed. 425 * @access public 426 */ 427 function setMaximumNestedFrames($max) { 428 $this->_maximum_nested_frames = $max; 429 } 430 431 /** 432 * Sets the socket timeout for opening a connection. 433 * @param integer $timeout Maximum time in seconds. 434 * @access public 435 */ 436 function setConnectionTimeout($timeout) { 437 $this->_user_agent->setConnectionTimeout($timeout); 438 } 439 440 /** 441 * Sets proxy to use on all requests for when 442 * testing from behind a firewall. Set URL 443 * to false to disable. 444 * @param string $proxy Proxy URL. 445 * @param string $username Proxy username for authentication. 446 * @param string $password Proxy password for authentication. 447 * @access public 448 */ 449 function useProxy($proxy, $username = false, $password = false) { 450 $this->_user_agent->useProxy($proxy, $username, $password); 451 } 452 453 /** 454 * Fetches the page content with a HEAD request. 455 * Will affect cookies, but will not change the base URL. 456 * @param string/SimpleUrl $url Target to fetch as string. 457 * @param hash/SimpleHeadEncoding $parameters Additional parameters for 458 * HEAD request. 459 * @return boolean True if successful. 460 * @access public 461 */ 462 function head($url, $parameters = false) { 463 if (! is_object($url)) { 464 $url = new SimpleUrl($url); 465 } 466 if ($this->getUrl()) { 467 $url = $url->makeAbsolute($this->getUrl()); 468 } 469 $response = &$this->_user_agent->fetchResponse($url, new SimpleHeadEncoding($parameters)); 470 return ! $response->isError(); 471 } 472 473 /** 474 * Fetches the page content with a simple GET request. 475 * @param string/SimpleUrl $url Target to fetch. 476 * @param hash/SimpleFormEncoding $parameters Additional parameters for 477 * GET request. 478 * @return string Content of page or false. 479 * @access public 480 */ 481 function get($url, $parameters = false) { 482 if (! is_object($url)) { 483 $url = new SimpleUrl($url); 484 } 485 if ($this->getUrl()) { 486 $url = $url->makeAbsolute($this->getUrl()); 487 } 488 return $this->_load($url, new SimpleGetEncoding($parameters)); 489 } 490 491 /** 492 * Fetches the page content with a POST request. 493 * @param string/SimpleUrl $url Target to fetch as string. 494 * @param hash/SimpleFormEncoding $parameters POST parameters. 495 * @return string Content of page. 496 * @access public 497 */ 498 function post($url, $parameters = false) { 499 if (! is_object($url)) { 500 $url = new SimpleUrl($url); 501 } 502 if ($this->getUrl()) { 503 $url = $url->makeAbsolute($this->getUrl()); 504 } 505 return $this->_load($url, new SimplePostEncoding($parameters)); 506 } 507 508 /** 509 * Equivalent to hitting the retry button on the 510 * browser. Will attempt to repeat the page fetch. If 511 * there is no history to repeat it will give false. 512 * @return string/boolean Content if fetch succeeded 513 * else false. 514 * @access public 515 */ 516 function retry() { 517 $frames = $this->_page->getFrameFocus(); 518 if (count($frames) > 0) { 519 $this->_loadFrame( 520 $frames, 521 $this->_page->getUrl(), 522 $this->_page->getRequestData()); 523 return $this->_page->getRaw(); 524 } 525 if ($url = $this->_history->getUrl()) { 526 $this->_page = &$this->_fetch($url, $this->_history->getParameters()); 527 return $this->_page->getRaw(); 528 } 529 return false; 530 } 531 532 /** 533 * Equivalent to hitting the back button on the 534 * browser. The browser history is unchanged on 535 * failure. The page content is refetched as there 536 * is no concept of content caching in SimpleTest. 537 * @return boolean True if history entry and 538 * fetch succeeded 539 * @access public 540 */ 541 function back() { 542 if (! $this->_history->back()) { 543 return false; 544 } 545 $content = $this->retry(); 546 if (! $content) { 547 $this->_history->forward(); 548 } 549 return $content; 550 } 551 552 /** 553 * Equivalent to hitting the forward button on the 554 * browser. The browser history is unchanged on 555 * failure. The page content is refetched as there 556 * is no concept of content caching in SimpleTest. 557 * @return boolean True if history entry and 558 * fetch succeeded