Blame | Letzte Änderung | Log anzeigen | RSS feed
<?php/*** Base include file for SimpleTest* @package SimpleTest* @subpackage WebTester* @version $Id: page.php 1398 2006-09-08 19:31:03Z xue $*//**#@+* include other SimpleTest class files*/require_once(dirname(__FILE__) . '/http.php');require_once(dirname(__FILE__) . '/parser.php');require_once(dirname(__FILE__) . '/tag.php');require_once(dirname(__FILE__) . '/form.php');require_once(dirname(__FILE__) . '/selector.php');/**#@-*//*** Creates tags and widgets given HTML tag* attributes.* @package SimpleTest* @subpackage WebTester*/class SimpleTagBuilder {/*** Factory for the tag objects. Creates the* appropriate tag object for the incoming tag name* and attributes.* @param string $name HTML tag name.* @param hash $attributes Element attributes.* @return SimpleTag Tag object.* @access public*/function createTag($name, $attributes) {static $map = array('a' => 'SimpleAnchorTag','title' => 'SimpleTitleTag','button' => 'SimpleButtonTag','textarea' => 'SimpleTextAreaTag','option' => 'SimpleOptionTag','label' => 'SimpleLabelTag','form' => 'SimpleFormTag','frame' => 'SimpleFrameTag');$attributes = $this->_keysToLowerCase($attributes);if (array_key_exists($name, $map)) {$tag_class = $map[$name];return new $tag_class($attributes);} elseif ($name == 'select') {return $this->_createSelectionTag($attributes);} elseif ($name == 'input') {return $this->_createInputTag($attributes);}return new SimpleTag($name, $attributes);}/*** Factory for selection fields.* @param hash $attributes Element attributes.* @return SimpleTag Tag object.* @access protected*/function _createSelectionTag($attributes) {if (isset($attributes['multiple'])) {return new MultipleSelectionTag($attributes);}return new SimpleSelectionTag($attributes);}/*** Factory for input tags.* @param hash $attributes Element attributes.* @return SimpleTag Tag object.* @access protected*/function _createInputTag($attributes) {if (! isset($attributes['type'])) {return new SimpleTextTag($attributes);}$type = strtolower(trim($attributes['type']));$map = array('submit' => 'SimpleSubmitTag','image' => 'SimpleImageSubmitTag','checkbox' => 'SimpleCheckboxTag','radio' => 'SimpleRadioButtonTag','text' => 'SimpleTextTag','hidden' => 'SimpleTextTag','password' => 'SimpleTextTag','file' => 'SimpleUploadTag');if (array_key_exists($type, $map)) {$tag_class = $map[$type];return new $tag_class($attributes);}return false;}/*** Make the keys lower case for case insensitive look-ups.* @param hash $map Hash to convert.* @return hash Unchanged values, but keys lower case.* @access private*/function _keysToLowerCase($map) {$lower = array();foreach ($map as $key => $value) {$lower[strtolower($key)] = $value;}return $lower;}}/*** SAX event handler. Maintains a list of* open tags and dispatches them as they close.* @package SimpleTest* @subpackage WebTester*/class SimplePageBuilder extends SimpleSaxListener {protected $_tags;protected $_page;protected $_private_content_tag;/*** Sets the builder up empty.* @access public*/function SimplePageBuilder() {$this->SimpleSaxListener();}/*** Frees up any references so as to allow the PHP garbage* collection from unset() to work.* @access public*/function free() {unset($this->_tags);unset($this->_page);unset($this->_private_content_tags);}/*** Reads the raw content and send events* into the page to be built.* @param $response SimpleHttpResponse Fetched response.* @return SimplePage Newly parsed page.* @access public*/function &parse($response) {$this->_tags = array();$this->_page = $this->_createPage($response);$parser = $this->_createParser($this);$parser->parse($response->getContent());$this->_page->acceptPageEnd();return $this->_page;}/*** Creates an empty page.* @return SimplePage New unparsed page.* @access protected*/function &_createPage($response) {$page = new SimplePage($response);return $page;}/*** Creates the parser used with the builder.* @param $listener SimpleSaxListener Target of parser.* @return SimpleSaxParser Parser to generate* events for the builder.* @access protected*/function &_createParser($listener) {$parser = new SimpleHtmlSaxParser($listener);return $parser;}/*** Start of element event. Opens a new tag.* @param string $name Element name.* @param hash $attributes Attributes without content* are marked as true.* @return boolean False on parse error.* @access public*/function startElement($name, $attributes) {$factory = new SimpleTagBuilder();$tag = $factory->createTag($name, $attributes);if (! $tag) {return true;}if ($tag->getTagName() == 'label') {$this->_page->acceptLabelStart($tag);$this->_openTag($tag);return true;}if ($tag->getTagName() == 'form') {$this->_page->acceptFormStart($tag);return true;}if ($tag->getTagName() == 'frameset') {$this->_page->acceptFramesetStart($tag);return true;}if ($tag->getTagName() == 'frame') {$this->_page->acceptFrame($tag);return true;}if ($tag->isPrivateContent() && ! isset($this->_private_content_tag)) {$this->_private_content_tag = $tag;}if ($tag->expectEndTag()) {$this->_openTag($tag);return true;}$this->_page->acceptTag($tag);return true;}/*** End of element event.* @param string $name Element name.* @return boolean False on parse error.* @access public*/function endElement($name) {if ($name == 'label') {$this->_page->acceptLabelEnd();return true;}if ($name == 'form') {$this->_page->acceptFormEnd();return true;}if ($name == 'frameset') {$this->_page->acceptFramesetEnd();return true;}if ($this->_hasNamedTagOnOpenTagStack($name)) {$tag = array_pop($this->_tags[$name]);if ($tag->isPrivateContent() && $this->_private_content_tag->getTagName() == $name) {unset($this->_private_content_tag);}$this->_addContentTagToOpenTags($tag);$this->_page->acceptTag($tag);return true;}return true;}/*** Test to see if there are any open tags awaiting* closure that match the tag name.* @param string $name Element name.* @return boolean True if any are still open.* @access private*/function _hasNamedTagOnOpenTagStack($name) {return isset($this->_tags[$name]) && (count($this->_tags[$name]) > 0);}/*** Unparsed, but relevant data. The data is added* to every open tag.* @param string $text May include unparsed tags.* @return boolean False on parse error.* @access public*/function addContent($text) {if (isset($this->_private_content_tag)) {$this->_private_content_tag->addContent($text);} else {$this->_addContentToAllOpenTags($text);}return true;}/*** Any content fills all currently open tags unless it* is part of an option tag.* @param string $text May include unparsed tags.* @access private*/function _addContentToAllOpenTags($text) {foreach (array_keys($this->_tags) as $name) {for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {$this->_tags[$name][$i]->addContent($text);}}}/*** Parsed data in tag form. The parsed tag is added* to every open tag. Used for adding options to select* fields only.* @param SimpleTag $tag Option tags only.* @access private*/function _addContentTagToOpenTags($tag) {if ($tag->getTagName() != 'option') {return;}foreach (array_keys($this->_tags) as $name) {for ($i = 0, $count = count($this->_tags[$name]); $i < $count; $i++) {$this->_tags[$name][$i]->addTag($tag);}}}/*** Opens a tag for receiving content. Multiple tags* will be receiving input at the same time.* @param SimpleTag $tag New content tag.* @access private*/function _openTag($tag) {$name = $tag->getTagName();if (! in_array($name, array_keys($this->_tags))) {$this->_tags[$name] = array();}$this->_tags[$name][] = $tag;}}/*** A wrapper for a web page.* @package SimpleTest* @subpackage WebTester*/class SimplePage {protected $_links;protected $_title;protected $_last_widget;protected $_label;protected $_left_over_labels;protected $_open_forms;protected $_complete_forms;protected $_frameset;protected $_frames;protected $_frameset_nesting_level;protected $_transport_error;protected $_raw;protected $_text;protected $_sent;protected $_headers;protected $_method;protected $_url;protected $_request_data;/*** Parses a page ready to access it's contents.* @param SimpleHttpResponse $response Result of HTTP fetch.* @access public*/function SimplePage($response = false) {$this->_links = array();$this->_title = false;$this->_left_over_labels = array();$this->_open_forms = array();$this->_complete_forms = array();$this->_frameset = false;$this->_frames = array();$this->_frameset_nesting_level = 0;$this->_text = false;if ($response) {$this->_extractResponse($response);} else {$this->_noResponse();}}/*** Extracts all of the response information.* @param SimpleHttpResponse $response Response being parsed.* @access private*/function _extractResponse($response) {$this->_transport_error = $response->getError();$this->_raw = $response->getContent();$this->_sent = $response->getSent();$this->_headers = $response->getHeaders();$this->_method = $response->getMethod();$this->_url = $response->getUrl();$this->_request_data = $response->getRequestData();}/*** Sets up a missing response.* @access private*/function _noResponse() {$this->_transport_error = 'No page fetched yet';$this->_raw = false;$this->_sent = false;$this->_headers = false;$this->_method = 'GET';$this->_url = false;$this->_request_data = false;}/*** Original request as bytes sent down the wire.* @return mixed Sent content.* @access public*/function getRequest() {return $this->_sent;}/*** Accessor for raw text of page.* @return string Raw unparsed content.* @access public*/function getRaw() {return $this->_raw;}/*** Accessor for plain text of page as a text browser* would see it.* @return string Plain text of page.* @access public*/function getText() {if (! $this->_text) {$this->_text = SimpleHtmlSaxParser::normalise($this->_raw);}return $this->_text;}/*** Accessor for raw headers of page.* @return string Header block as text.* @access public*/function getHeaders() {if ($this->_headers) {return $this->_headers->getRaw();}return false;}/*** Original request method.* @return string GET, POST or HEAD.* @access public*/function getMethod() {return $this->_method;}/*** Original resource name.* @return SimpleUrl Current url.* @access public*/function getUrl() {return $this->_url;}/*** Original request data.* @return mixed Sent content.* @access public*/function getRequestData() {return $this->_request_data;}/*** Accessor for last error.* @return string Error from last response.* @access public*/function getTransportError() {return $this->_transport_error;}/*** Accessor for current MIME type.* @return string MIME type as string; e.g. 'text/html'* @access public*/function getMimeType() {if ($this->_headers) {return $this->_headers->getMimeType();}return false;}/*** Accessor for HTTP response code.* @return integer HTTP response code received.* @access public*/function getResponseCode() {if ($this->_headers) {return $this->_headers->getResponseCode();}return false;}/*** Accessor for last Authentication type. Only valid* straight after a challenge (401).* @return string Description of challenge type.* @access public*/function getAuthentication() {if ($this->_headers) {return $this->_headers->getAuthentication();}return false;}/*** Accessor for last Authentication realm. Only valid* straight after a challenge (401).* @return string Name of security realm.* @access public*/function getRealm() {if ($this->_headers) {return $this->_headers->getRealm();}return false;}/*** Accessor for current frame focus. Will be* false as no frames.* @return array Always empty.* @access public*/function getFrameFocus() {return array();}/*** Sets the focus by index. The integer index starts from 1.* @param integer $choice Chosen frame.* @return boolean Always false.* @access public*/function setFrameFocusByIndex($choice) {return false;}/*** Sets the focus by name. Always fails for a leaf page.* @param string $name Chosen frame.* @return boolean False as no frames.* @access public*/function setFrameFocus($name) {return false;}/*** Clears the frame focus. Does nothing for a leaf page.* @access public*/function clearFrameFocus() {}/*** Adds a tag to the page.* @param SimpleTag $tag Tag to accept.* @access public*/function acceptTag($tag) {if ($tag->getTagName() == "a") {$this->_addLink($tag);} elseif ($tag->getTagName() == "title") {$this->_setTitle($tag);} elseif ($this->_isFormElement($tag->getTagName())) {for ($i = 0; $i < count($this->_open_forms); $i++) {$this->_open_forms[$i]->addWidget($tag);}$this->_last_widget = $tag;}}/*** Opens a label for a described widget.* @param SimpleFormTag $tag Tag to accept.* @access public*/function acceptLabelStart($tag) {$this->_label = $tag;unset($this->_last_widget);}/*** Closes the most recently opened label.* @access public*/function acceptLabelEnd() {if (isset($this->_label)) {if (isset($this->_last_widget)) {$this->_last_widget->setLabel($this->_label->getText());unset($this->_last_widget);} else {$this->_left_over_labels[] = SimpleTestCompatibility::copy($this->_label);}unset($this->_label);}}/*** Tests to see if a tag is a possible form* element.* @param string $name HTML element name.* @return boolean True if form element.* @access private*/function _isFormElement($name) {return in_array($name, array('input', 'button', 'textarea', 'select'));}/*** Opens a form. New widgets go here.* @param SimpleFormTag $tag Tag to accept.* @access public*/function acceptFormStart($tag) {$this->_open_forms[] = new SimpleForm($tag, $this->getUrl());}/*** Closes the most recently opened form.* @access public*/function acceptFormEnd() {if (count($this->_open_forms)) {$this->_complete_forms[] = array_pop($this->_open_forms);}}/*** Opens a frameset. A frameset may contain nested* frameset tags.* @param SimpleFramesetTag $tag Tag to accept.* @access public*/function acceptFramesetStart($tag) {if (! $this->_isLoadingFrames()) {$this->_frameset = $tag;}$this->_frameset_nesting_level++;}/*** Closes the most recently opened frameset.* @access public*/function acceptFramesetEnd() {if ($this->_isLoadingFrames()) {$this->_frameset_nesting_level--;}}/*** Takes a single frame tag and stashes it in* the current frame set.* @param SimpleFrameTag $tag Tag to accept.* @access public*/function acceptFrame($tag) {if ($this->_isLoadingFrames()) {if ($tag->getAttribute('src')) {$this->_frames[] = $tag;}}}/*** Test to see if in the middle of reading* a frameset.* @return boolean True if inframeset.* @access private*/function _isLoadingFrames() {if (! $this->_frameset) {return false;}return ($this->_frameset_nesting_level > 0);}/*** Test to see if link is an absolute one.* @param string $url Url to test.* @return boolean True if absolute.* @access protected*/function _linkIsAbsolute($url) {$parsed = new SimpleUrl($url);return (boolean)($parsed->getScheme() && $parsed->getHost());}/*** Adds a link to the page.* @param SimpleAnchorTag $tag Link to accept.* @access protected*/function _addLink($tag) {$this->_links[] = $tag;}/*** Marker for end of complete page. Any work in* progress can now be closed.* @access public*/function acceptPageEnd() {while (count($this->_open_forms)) {$this->_complete_forms[] = array_pop($this->_open_forms);}foreach ($this->_left_over_labels as $label) {for ($i = 0, $count = count($this->_complete_forms); $i < $count; $i++) {$this->_complete_forms[$i]->attachLabelBySelector(new SimpleById($label->getFor()),$label->getText());}}}/*** Test for the presence of a frameset.* @return boolean True if frameset.* @access public*/function hasFrames() {return (boolean)$this->_frameset;}/*** Accessor for frame name and source URL for every frame that* will need to be loaded. Immediate children only.* @return boolean/array False if no frameset or* otherwise a hash of frame URLs.* The key is either a numerical* base one index or the name attribute.* @access public*/function getFrameset() {if (! $this->_frameset) {return false;}$urls = array();for ($i = 0; $i < count($this->_frames); $i++) {$name = $this->_frames[$i]->getAttribute('name');$url = new SimpleUrl($this->_frames[$i]->getAttribute('src'));$urls[$name ? $name : $i + 1] = $url->makeAbsolute($this->getUrl());}return $urls;}/*** Fetches a list of loaded frames.* @return array/string Just the URL for a single page.* @access public*/function getFrames() {$url = $this->getUrl();return $url->asString();}/*** Accessor for a list of all fixed links.* @return array List of urls with scheme of* http or https and hostname.* @access public*/function getAbsoluteUrls() {$all = array();foreach ($this->_links as $link) {if ($this->_linkIsAbsolute($link->getHref())) {$all[] = $link->getHref();}}return $all;}/*** Accessor for a list of all relative links.* @return array List of urls without hostname.* @access public*/function getRelativeUrls() {$all = array();foreach ($this->_links as $link) {if (! $this->_linkIsAbsolute($link->getHref())) {$all[] = $link->getHref();}}return $all;}/*** Accessor for URLs by the link label. Label will match* regardess of whitespace issues and case.* @param string $label Text of link.* @return array List of links with that label.* @access public*/function getUrlsByLabel($label) {$matches = array();foreach ($this->_links as $link) {if ($link->getText() == $label) {$matches[] = $this->_getUrlFromLink($link);}}return $matches;}/*** Accessor for a URL by the id attribute.* @param string $id Id attribute of link.* @return SimpleUrl URL with that id of false if none.* @access public*/function getUrlById($id) {foreach ($this->_links as $link) {if ($link->getAttribute('id') === (string)$id) {return $this->_getUrlFromLink($link);}}return false;}/*** Converts a link into a target URL.* @param SimpleAnchor $link Parsed link.* @return SimpleUrl URL with frame target if any.* @access private*/function _getUrlFromLink($link) {$url = $this->_makeAbsolute($link->getHref());if ($link->getAttribute('target')) {$url->setTarget($link->getAttribute('target'));}return $url;}/*** Expands expandomatic URLs into fully qualified* URLs.* @param SimpleUrl $url Relative URL.* @return SimpleUrl Absolute URL.* @access protected*/function _makeAbsolute($url) {if (! is_object($url)) {$url = new SimpleUrl($url);}return $url->makeAbsolute($this->getUrl());}/*** Sets the title tag contents.* @param SimpleTitleTag $tag Title of page.* @access protected*/function _setTitle($tag) {$this->_title = $tag;}/*** Accessor for parsed title.* @return string Title or false if no title is present.* @access public*/function getTitle() {if ($this->_title) {return $this->_title->getText();}return false;}/*** Finds a held form by button label. Will only* search correctly built forms.* @param SimpleSelector $selector Button finder.* @return SimpleForm Form object containing* the button.* @access public*/function &getFormBySubmit($selector) {for ($i = 0; $i < count($this->_complete_forms); $i++) {if ($this->_complete_forms[$i]->hasSubmit($selector)) {return $this->_complete_forms[$i];}}$null = null;return $null;}/*** Finds a held form by image using a selector.* Will only search correctly built forms.* @param SimpleSelector $selector Image finder.* @return SimpleForm Form object containing* the image.* @access public*/function &getFormByImage($selector) {for ($i = 0; $i < count($this->_complete_forms); $i++) {if ($this->_complete_forms[$i]->hasImage($selector)) {return $this->_complete_forms[$i];}}$null = null;return $null;}/*** Finds a held form by the form ID. A way of* identifying a specific form when we have control* of the HTML code.* @param string $id Form label.* @return SimpleForm Form object containing the matching ID.* @access public*/function &getFormById($id) {for ($i = 0; $i < count($this->_complete_forms); $i++) {if ($this->_complete_forms[$i]->getId() == $id) {return $this->_complete_forms[$i];}}$null = null;return $null;}/*** Sets a field on each form in which the field is* available.* @param SimpleSelector $selector Field finder.* @param string $value Value to set field to.* @return boolean True if value is valid.* @access public*/function setField($selector, $value) {$is_set = false;for ($i = 0; $i < count($this->_complete_forms); $i++) {if ($this->_complete_forms[$i]->setField($selector, $value)) {$is_set = true;}}return $is_set;}/*** Accessor for a form element value within a page.* @param SimpleSelector $selector Field finder.* @return string/boolean A string if the field is* present, false if unchecked* and null if missing.* @access public*/function getField($selector) {for ($i = 0; $i < count($this->_complete_forms); $i++) {$value = $this->_complete_forms[$i]->getValue($selector);if (isset($value)) {return $value;}}return null;}}?>