| [ 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: page.php 5999 2007-06-18 13:13:08Z pachanga $ 7 */ 8 9 /**#@+ 10 * include other SimpleTest class files 11 */ 12 require_once(dirname(__FILE__) . '/http.php'); 13 require_once(dirname(__FILE__) . '/parser.php'); 14 require_once(dirname(__FILE__) . '/tag.php'); 15 require_once(dirname(__FILE__) . '/form.php'); 16 require_once(dirname(__FILE__) . '/selector.php'); 17 /**#@-*/ 18 19 /** 20 * Creates tags and widgets given HTML tag 21 * attributes. 22 * @package SimpleTest 23 * @subpackage WebTester 24 */ 25 class SimpleTagBuilder { 26 27 /** 28 * Factory for the tag objects. Creates the 29 * appropriate tag object for the incoming tag name 30 * and attributes. 31 * @param string $name HTML tag name. 32 * @param hash $attributes Element attributes. 33 * @return SimpleTag Tag object. 34 * @access public 35 */ 36 function createTag($name, $attributes) { 37 static $map = array( 38 'a' => 'SimpleAnchorTag', 39 'title' => 'SimpleTitleTag', 40 'button' => 'SimpleButtonTag', 41 'textarea' => 'SimpleTextAreaTag', 42 'option' => 'SimpleOptionTag', 43 'label' => 'SimpleLabelTag', 44 'form' => 'SimpleFormTag', 45 'frame' => 'SimpleFrameTag'); 46 $attributes = $this->_keysToLowerCase($attributes); 47 if (array_key_exists($name, $map)) { 48 $tag_class = $map[$name]; 49 return new $tag_class($attributes); 50 } elseif ($name == 'select') { 51 return $this->_createSelectionTag($attributes); 52 } elseif ($name == 'input') { 53 return $this->_createInputTag($attributes); 54 } 55 return new SimpleTag($name, $attributes); 56 } 57 58 /** 59 * Factory for selection fields. 60 * @param hash $attributes Element attributes. 61 * @return SimpleTag Tag object. 62 * @access protected 63 */ 64 function _createSelectionTag($attributes) { 65 if (isset($attributes['multiple'])) { 66 return new MultipleSelectionTag($attributes); 67 } 68 return new SimpleSelectionTag($attributes); 69 } 70 71 /** 72 * Factory for input tags. 73 * @param hash $attributes Element attributes. 74 * @return SimpleTag Tag object. 75 * @access protected 76 */ 77 function _createInputTag($attributes) { 78 if (! isset($attributes['type'])) { 79 return new SimpleTextTag($attributes); 80 } 81 $type = strtolower(trim($attributes['type'])); 82 $map = array( 83 'submit' => 'SimpleSubmitTag', 84 'image' => 'SimpleImageSubmitTag', 85 'checkbox' => 'SimpleCheckboxTag', 86 'radio' => 'SimpleRadioButtonTag', 87 'text' => 'SimpleTextTag', 88 'hidden' => 'SimpleTextTag', 89 'password' => 'SimpleTextTag', 90 'file' => 'SimpleUploadTag'); 91 if (array_key_exists($type, $map)) { 92 $tag_class = $map[$type]; 93 return new $tag_class($attributes); 94 } 95 return false; 96 } 97 98 /** 99 * Make the keys lower case for case insensitive look-ups. 100 * @param hash $map Hash to convert. 101 * @return hash Unchanged values, but keys lower case. 102 * @access private 103 */ 104 function _keysToLowerCase($map) { 105 $lower = array(); 106 foreach ($map as $key => $value) { 107 $lower[strtolower($key)] = $value; 108 } 109 return $lower; 110 } 111 } 112 113 /** 114 * SAX event handler. Maintains a list of 115 * open tags and dispatches them as they close. 116 * @package SimpleTest 117 * @subpackage WebTester 118 */ 119 class SimplePageBuilder extends SimpleSaxListener { 120 var $_tags; 121 var $_page; 122 var $_private_content_tag; 123 124 /** 125 * Sets the builder up empty. 126 * @access public 127 */ 128 function SimplePageBuilder() { 129 $this->SimpleSaxListener(); 130 } 131 132 /** 133 * Frees up any references so as to allow the PHP garbage 134 * collection from unset() to work. 135 * @access public 136 */ 137 function free() { 138 unset($this->_tags); 139 unset($this->_page); 140 unset($this->_private_content_tags); 141 } 142 143 /** 144 * Reads the raw content and send events 145 * into the page to be built. 146 * @param $response SimpleHttpResponse Fetched response. 147 * @return SimplePage Newly parsed page. 148 * @access public 149 */ 150 function &parse($response) { 151 $this->_tags = array(); 152 $this->_page = &$this->_createPage($response); 153 $parser = &$this->_createParser($this); 154 $parser->parse($response->getContent()); 155 $this->_page->acceptPageEnd(); 156 return $this->_page; 157 } 158 159 /** 160 * Creates an empty page. 161 * @return SimplePage New unparsed page. 162 * @access protected 163 */ 164 function &_createPage($response) { 165 $page = &new SimplePage($response); 166 return $page; 167 } 168 169 /** 170 * Creates the parser used with the builder. 171 * @param $listener SimpleSaxListener Target of parser. 172 * @return SimpleSaxParser Parser to generate 173 * events for the builder. 174 * @access protected 175 */ 176 function &_createParser(&$listener) { 177 $parser = &new SimpleHtmlSaxParser($listener); 178 return $parser; 179 } 180 181 /** 182 * Start of element event. Opens a new tag. 183 * @param string $name Element name. 184 * @param hash $attributes Attributes without content 185 * are marked as true. 186 * @return boolean False on parse error. 187 * @access public 188 */ 189 function startElement($name, $attributes) { 190 $factory = &new SimpleTagBuilder(); 191 $tag = $factory->createTag($name, $attributes); 192 if (! $tag) { 193 return true; 194 } 195 if ($tag->getTagName() == 'label') { 196 $this->_page->acceptLabelStart($tag); 197 $this->_openTag($tag); 198 return true; 199 } 200 if ($tag->getTagName() == 'form') { 201 $this->_page->acceptFormStart($tag); 202 return true; 203 } 204 if ($tag->getTagName() == 'frameset') { 205 $this->_page->acceptFramesetStart($tag); 206 return true; 207 } 208 if ($tag->getTagName() == 'frame') { 209 $this->_page->acceptFrame($tag); 210 return true; 211 } 212 if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) { 213 $this->_private_content_tag = &$tag; 214 } 215 if ($tag->expectEndTag()) { 216 $this->_openTag($tag); 217 return true; 218 } 219 $this->_page->acceptTag($tag); 220 return true; 221 } 222 223 /** 224 * End of element event. 225 * @param string $name Element name. 226 * @return boolean False on parse error. 227 * @access public 228 */ 229 function endElement($name) { 230 if ($name == 'label') { 231 $this->_page->acceptLabelEnd(); 232 return true; 233 } 234 if ($name == 'form') { 235 $this->_page->acceptFormEnd(); 236 return true; 237 } 238 if ($name == 'frameset') { 239 $this->_page->acceptFramesetEnd(); 240 return true; 241 } 242 if ($this->_hasNamedTagOnOpenTagStack($name)) { 243 $tag = array_pop($this->_tags[$name]); 244 if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) { 245 unset($this->_private_content_tag); 246 } 247 $this->_addContentTagToOpenTags($tag); 248 $this->_page->acceptTag($tag); 249 return true; 250 } 251 return true; 252 } 253 254 /** 255 * Test to see if there are any open tags awaiting 256 * closure that match the tag name. 257 * @param string $name Element name. 258 * @return boolean True if any are still open. 259 * @access private 260 */ 261 function _hasNamedTagOnOpenTagStack($name) { 262 return isset($this->_tags[$name]) && (count($this->_tags[$name]) > 0); 263 } 264 265 /** 266 * Unparsed, but relevant data. The data is added 267 * to every open tag. 268 * @param string $text May include unparsed tags. 269 * @return boolean False on parse error. 270 * @access public 271 */ 272 function addContent($text) { 273 if (isset($this->_private_content_tag)) { 274 $this->_private_content_tag->addContent($text); 275 } else { 276 $this->_addContentToAllOpenTags($text); 277 } 278 return true; 279 } 280 281 /** 282 * Any content fills all currently open tags unless it 283 * is part of an option tag. 284 * @param string $text May include unparsed tags. 285 * @access private 286 */ 287 function _addContentToAllOpenTags($text) { 288 foreach (array_keys($this->_tags) as $name) { 289 for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) { 290 $this->_tags[$name][$i]->addContent($text); 291 } 292 } 293 } 294 295 /** 296 * Parsed data in tag form. The parsed tag is added 297 * to every open tag. Used for adding options to select 298 * fields only. 299 * @param SimpleTag $tag Option tags only. 300 * @access private 301 */ 302 function _addContentTagToOpenTags(&$tag) { 303 if ($tag->getTagName() != 'option') { 304 return; 305 } 306 foreach (array_keys($this->_tags) as $name) { 307 for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) { 308 $this->_tags[$name][$i]->addTag($tag); 309 } 310 } 311 } 312 313 /** 314 * Opens a tag for receiving content. Multiple tags 315 * will be receiving input at the same time. 316 * @param SimpleTag $tag New content tag. 317 * @access private 318 */ 319 function _openTag(&$tag) { 320 $name = $tag->getTagName(); 321 if (! in_array($name, array_keys($this->_tags))) { 322 $this->_tags[$name] = array(); 323 } 324 $this->_tags[$name][] = &$tag; 325 } 326 } 327 328 /** 329 * A wrapper for a web page. 330 * @package SimpleTest 331 * @subpackage WebTester 332 */ 333 class SimplePage { 334 var $_links; 335 var $_title; 336 var $_last_widget; 337 var $_label; 338 var $_left_over_labels; 339 var $_open_forms; 340 var $_complete_forms; 341 var $_frameset; 342 var $_frames; 343 var $_frameset_nesting_level; 344 var $_transport_error; 345 var $_raw; 346 var $_text; 347 var $_sent; 348 var $_headers; 349 var $_method; 350 var $_url; 351 var $_request_data; 352 353 /** 354 * Parses a page ready to access it's contents. 355 * @param SimpleHttpResponse $response Result of HTTP fetch. 356 * @access public 357 */ 358 function SimplePage($response = false) { 359 $this->_links = array(); 360 $this->_title = false; 361 $this->_left_over_labels = array(); 362 $this->_open_forms = array(); 363 $this->_complete_forms = array(); 364 $this->_frameset = false; 365 $this->_frames = array(); 366 $this->_frameset_nesting_level = 0; 367 $this->_text = false; 368 if ($response) { 369 $this->_extractResponse($response); 370 } else { 371 $this->_noResponse(); 372 } 373 } 374 375 /** 376 * Extracts all of the response information. 377 * @param SimpleHttpResponse $response Response being parsed. 378 * @access private 379 */ 380 function _extractResponse($response) { 381 $this->_transport_error = $response->getError(); 382 $this->_raw = $response->getContent(); 383 $this->_sent = $response->getSent(); 384 $this->_headers = $response->getHeaders(); 385 $this->_method = $response->getMethod(); 386 $this->_url = $response->getUrl(); 387 $this->_request_data = $response->getRequestData(); 388 } 389 390 /** 391 * Sets up a missing response. 392 * @access private 393 */ 394 function _noResponse() { 395 $this->_transport_error = 'No page fetched yet'; 396 $this->_raw = false; 397 $this->_sent = false; 398 $this->_headers = false; 399 $this->_method = 'GET'; 400 $this->_url = false; 401 $this->_request_data = false; 402 } 403 404 /** 405 * Original request as bytes sent down the wire. 406 * @return mixed Sent content. 407 * @access public 408 */ 409 function getRequest() { 410 return $this->_sent; 411 } 412 413 /** 414 * Accessor for raw text of page. 415 * @return string Raw unparsed content. 416 * @access public 417 */ 418 function getRaw() { 419 return $this->_raw; 420 } 421 422 /** 423 * Accessor for plain text of page as a text browser 424 * would see it. 425 * @return string Plain text of page. 426 * @access public 427 */ 428 function getText() { 429 if (! $this->_text) { 430 $this->_text = SimpleHtmlSaxParser::normalise($this->_raw); 431 } 432 return $this->_text; 433 } 434 435 /** 436 * Accessor for raw headers of page. 437 * @return string Header block as text. 438 * @access public 439 */ 440 function getHeaders() { 441 if ($this->_headers) { 442 return $this->_headers->getRaw(); 443 } 444 return false; 445 } 446 447 /** 448 * Original request method. 449 * @return string GET, POST or HEAD. 450 * @access public 451 */ 452 function getMethod() { 453 return $this->_method; 454 } 455 456 /** 457 * Original resource name. 458 * @return SimpleUrl Current url. 459 * @access public 460 */ 461 function getUrl() { 462 return $this->_url; 463 } 464 465 /** 466 * Original request data. 467 * @return mixed Sent content. 468 * @access public 469 */ 470 function getRequestData() { 471 return $this->_request_data; 472 } 473 474 /** 475 * Accessor for last error. 476 * @return string Error from last response. 477 * @access public 478 */ 479 function getTransportError() { 480 return $this->_transport_error; 481 } 482 483 /** 484 * Accessor for current MIME type. 485 * @return string MIME type as string; e.g. 'text/html' 486 * @access public 487 */ 488 function getMimeType() { 489 if ($this->_headers) { 490 return $this->_headers->getMimeType(); 491 } 492 return false; 493 } 494 495 /** 496 * Accessor for HTTP response code. 497 * @return integer HTTP response code received. 498 * @access public 499 */ 500 function getResponseCode() { 501 if ($this->_headers) { 502 return $this->_headers->getResponseCode(); 503 } 504 return false; 505 } 506 507 /** 508 * Accessor for last Authentication type. Only valid 509 * straight after a challenge (401). 510 * @return string Description of challenge type. 511 * @access public 512 */ 513 function getAuthentication() { 514 if ($this->_headers) { 515 return $this->_headers->getAuthentication(); 516 } 517 return false; 518 } 519 520 /** 521 * Accessor for last Authentication realm. Only valid 522 * straight after a challenge (401). 523 * @return string Name of security realm. 524 * @access public 525 */ 526 function getRealm() { 527 if ($this->_headers) { 528 return $this->_headers->getRealm(); 529 } 530 return false; 531 } 532 533 /** 534 * Accessor for current frame focus. Will be 535 * false as no frames. 536 * @return array Always empty. 537 * @access public 538 */ 539 function getFrameFocus() { 540 return array(); 541 } 542 543 /** 544 * Sets the focus by index. The integer index starts from 1. 545 * @param integer $choice Chosen frame. 546 * @return boolean Always false. 547 * @access public 548 */ 549 function setFrameFocusByIndex($choice) { 550 return false; 551 } 552 553 /** 554 * Sets the focus by name. Always fails for a leaf page. 555 * @param string $name Chosen frame. 556 * @return boolean False as no frames. 557 * @access public 558 */ 559 function setFrameFocus($name) { 560 return false; 561 } 562 563 /** 564 * Clears the frame focus. Does nothing for a leaf page. 565 * @access public 566 */ 567 function clearFrameFocus() { 568 } 569 570 /** 571 * Adds a tag to the page. 572 * @param SimpleTag $tag Tag to accept. 573 * @access public 574 */ 575 function acceptTag(&$tag) { 576 if ($tag->getTagName() == "a") {