Source for file browser.php

Documentation is available at browser.php

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