Source for file parser.php

Documentation is available at parser.php

  1. <?php
  2. /**
  3.  *  base include file for SimpleTest
  4.  *  @package    SimpleTest
  5.  *  @subpackage MockObjects
  6.  *  @version    $Id: parser.php 1723 2008-04-08 00:34:10Z lastcraft $
  7.  */
  8.  
  9. /**#@+
  10.  * Lexer mode stack constants
  11.  */
  12. foreach (array('LEXER_ENTER''LEXER_MATCHED',
  13.                 'LEXER_UNMATCHED''LEXER_EXIT',
  14.                 'LEXER_SPECIAL'as $i => $constant{
  15.     if (defined($constant)) {
  16.         define($constant$i 1);
  17.     }
  18. }
  19. /**#@-*/
  20.  
  21.  *    Compounded regular expression. Any of
  22.  *    the contained patterns could match and
  23.  *    when one does, it's label is returned.
  24.  *    @package SimpleTest
  25.  *    @subpackage WebTester
  26.  */
  27. class ParallelRegex {
  28.     var $_patterns;
  29.     var $_labels;
  30.     var $_regex;
  31.     var $_case;
  32.     
  33.     /**
  34.      *    Constructor. Starts with no patterns.
  35.      *    @param boolean $case    True for case sensitive, false
  36.      *                             for insensitive.
  37.      *    @access public
  38.      */
  39.     function ParallelRegex($case{
  40.         $this->_case = $case;
  41.         $this->_patterns = array();
  42.         $this->_labels = array();
  43.         $this->_regex = null;
  44.     }
  45.     
  46.     /**
  47.      *    Adds a pattern with an optional label.
  48.      *    @param string $pattern      Perl style regex, but ( and )
  49.      *                                 lose the usual meaning.
  50.      *    @param string $label        Label of regex to be returned
  51.      *                                 on a match.
  52.      *    @access public
  53.      */
  54.     function addPattern($pattern$label true{
  55.         $count count($this->_patterns);
  56.         $this->_patterns[$count$pattern;
  57.         $this->_labels[$count$label;
  58.         $this->_regex = null;
  59.     }
  60.     
  61.     /**
  62.      *    Attempts to match all patterns at once against
  63.      *    a string.
  64.      *    @param string $subject      String to match against.
  65.      *    @param string $match        First matched portion of
  66.      *                                 subject.
  67.      *    @return boolean             True on success.
  68.      *    @access public
  69.      */
  70.     function match($subject&$match{
  71.         if (count($this->_patterns== 0{
  72.             return false;
  73.         }
  74.         if (preg_match($this->_getCompoundedRegex()$subject$matches)) {
  75.             $match '';
  76.             return false;
  77.         }
  78.         $match $matches[0];
  79.         for ($i 1$i count($matches)$i++{
  80.             if ($matches[$i]{
  81.                 return $this->_labels[$i 1];
  82.             }
  83.         }
  84.         return true;
  85.     }
  86.     
  87.     /**
  88.      *    Compounds the patterns into a single
  89.      *    regular expression separated with the
  90.      *    "or" operator. Caches the regex.
  91.      *    Will automatically escape (, ) and / tokens.
  92.      *    @param array $patterns    List of patterns in order.
  93.      *    @access private
  94.      */
  95.     function _getCompoundedRegex({
  96.         if ($this->_regex == null{
  97.             for ($i 0$count count($this->_patterns)$i $count$i++{
  98.                 $this->_patterns[$i'(' str_replace(
  99.                         array('/''('')'),
  100.                         array('\/''\(''\)'),
  101.                         $this->_patterns[$i]')';
  102.             }
  103.             $this->_regex = "/" implode("|"$this->_patterns"/" $this->_getPerlMatchingFlags();
  104.         }
  105.         return $this->_regex;
  106.     }
  107.     
  108.     /**
  109.      *    Accessor for perl regex mode flags to use.
  110.      *    @return string       Perl regex flags.
  111.      *    @access private
  112.      */
  113.     function _getPerlMatchingFlags({
  114.         return ($this->_case ? "msS" "msSi");
  115.     }
  116. }
  117.  
  118. /**
  119.  *    States for a stack machine.
  120.  *    @package SimpleTest
  121.  *    @subpackage WebTester
  122.  */
  123.     var $_stack;
  124.     
  125.     /**
  126.      *    Constructor. Starts in named state.
  127.      *    @param string $start        Starting state name.
  128.      *    @access public
  129.      */
  130.     function SimpleStateStack($start{
  131.         $this->_stack = array($start);
  132.     }
  133.     
  134.     /**
  135.      *    Accessor for current state.
  136.      *    @return string       State.
  137.      *    @access public
  138.      */
  139.     function getCurrent({
  140.         return $this->_stack[count($this->_stack1];
  141.     }
  142.     
  143.     /**
  144.      *    Adds a state to the stack and sets it
  145.      *    to be the current state.
  146.      *    @param string $state        New state.
  147.      *    @access public
  148.      */
  149.     function enter($state{
  150.         array_push($this->_stack$state);
  151.     }
  152.     
  153.     /**
  154.      *    Leaves the current state and reverts
  155.      *    to the previous one.
  156.      *    @return boolean    False if we drop off
  157.      *                        the bottom of the list.
  158.      *    @access public
  159.      */
  160.     function leave({
  161.         if (count($this->_stack== 1{
  162.             return false;
  163.         }
  164.         array_pop($this->_stack);
  165.         return true;
  166.     }
  167. }
  168.  
  169. /**
  170.  *    Accepts text and breaks it into tokens.
  171.  *    Some optimisation to make the sure the
  172.  *    content is only scanned by the PHP regex
  173.  *    parser once. Lexer modes must not start
  174.  *    with leading underscores.
  175.  *    @package SimpleTest
  176.  *    @subpackage WebTester
  177.  */
  178. class SimpleLexer {
  179.     var $_regexes;
  180.     var $_parser;
  181.     var $_mode;
  182.     var $_mode_handlers;
  183.     var $_case;
  184.     
  185.     /**
  186.      *    Sets up the lexer in case insensitive matching
  187.      *    by default.
  188.      *    @param SimpleSaxParser $parser  Handling strategy by
  189.      *                                     reference.
  190.      *    @param string $start            Starting handler.
  191.      *    @param boolean $case            True for case sensitive.
  192.      *    @access public
  193.      */
  194.     function SimpleLexer(&$parser$start "accept"$case false{
  195.         $this->_case = $case;
  196.         $this->_regexes = array();
  197.         $this->_parser = &$parser;
  198.         $this->_mode = &new SimpleStateStack($start);
  199.         $this->_mode_handlers = array($start => $start);
  200.     }
  201.     
  202.     /**
  203.      *    Adds a token search pattern for a particular
  204.      *    parsing mode. The pattern does not change the
  205.      *    current mode.
  206.      *    @param string $pattern      Perl style regex, but ( and )
  207.      *                                 lose the usual meaning.
  208.      *    @param string $mode         Should only apply this
  209.      *                                 pattern when dealing with
  210.      *                                 this type of input.
  211.      *    @access public
  212.      */
  213.     function addPattern($pattern$mode "accept"{
  214.         if (isset($this->_regexes[$mode])) {
  215.             $this->_regexes[$modenew ParallelRegex($this->_case);
  216.         }
  217.         $this->_regexes[$mode]->addPattern($pattern);
  218.         if (isset($this->_mode_handlers[$mode])) {
  219.             $this->_mode_handlers[$mode$mode;
  220.         }
  221.     }
  222.     
  223.     /**
  224.      *    Adds a pattern that will enter a new parsing
  225.      *    mode. Useful for entering parenthesis, strings,
  226.      *    tags, etc.
  227.      *    @param string $pattern      Perl style regex, but ( and )
  228.      *                                 lose the usual meaning.
  229.      *    @param string $mode         Should only apply this
  230.      *                                 pattern when dealing with
  231.      *                                 this type of input.
  232.      *    @param string $new_mode     Change parsing to this new
  233.      *                                 nested mode.
  234.      *    @access public
  235.      */
  236.     function addEntryPattern($pattern$mode$new_mode{
  237.         if (isset($this->_regexes[$mode])) {
  238.             $this->_regexes[$modenew ParallelRegex($this->_case);
  239.         }
  240.         $this->_regexes[$mode]->addPattern($pattern$new_mode);
  241.         if (isset($this->_mode_handlers[$new_mode])) {
  242.             $this->_mode_handlers[$new_mode$new_mode;
  243.         }
  244.     }
  245.     
  246.     /**
  247.      *    Adds a pattern that will exit the current mode
  248.      *    and re-enter the previous one.
  249.      *    @param string $pattern      Perl style regex, but ( and )
  250.      *                                 lose the usual meaning.
  251.      *    @param string $mode         Mode to leave.
  252.      *    @access public
  253.      */
  254.     function addExitPattern($pattern$mode{
  255.         if (isset($this->_regexes[$mode])) {
  256.             $this->_regexes[$modenew ParallelRegex($this->_case);
  257.         }
  258.         $this->_regexes[$mode]->addPattern($pattern"__exit");
  259.         if (isset($this->_mode_handlers[$mode])) {
  260.             $this->_mode_handlers[$mode$mode;
  261.         }
  262.     }
  263.     
  264.     /**
  265.      *    Adds a pattern that has a special mode. Acts as an entry
  266.      *    and exit pattern in one go, effectively calling a special
  267.      *    parser handler for this token only.
  268.      *    @param string $pattern      Perl style regex, but ( and )
  269.      *                                 lose the usual meaning.
  270.      *    @param string $mode         Should only apply this
  271.      *                                 pattern when dealing with
  272.      *                                 this type of input.
  273.      *    @param string $special      Use this mode for this one token.
  274.      *    @access public
  275.      */
  276.     function addSpecialPattern($pattern$mode$special{
  277.         if (isset($this->_regexes[$mode])) {
  278.             $this->_regexes[$modenew ParallelRegex($this->_case);
  279.         }
  280.         $this->_regexes[$mode]->addPattern($pattern"_$special");
  281.         if (isset($this->_mode_handlers[$special])) {
  282.             $this->_mode_handlers[$special$special;
  283.         }
  284.     }
  285.     
  286.     /**
  287.      *    Adds a mapping from a mode to another handler.
  288.      *    @param string $mode        Mode to be remapped.
  289.      *    @param string $handler     New target handler.
  290.      *    @access public
  291.      */
  292.     function mapHandler($mode$handler{
  293.         $this->_mode_handlers[$mode$handler;
  294.     }
  295.     
  296.     /**
  297.      *    Splits the page text into tokens. Will fail
  298.      *    if the handlers report an error or if no
  299.      *    content is consumed. If successful then each
  300.      *    unparsed and parsed token invokes a call to the
  301.      *    held listener.
  302.      *    @param string $raw        Raw HTML text.
  303.      *    @return boolean           True on success, else false.
  304.      *    @access public
  305.      */
  306.     function parse($raw{
  307.         if (isset($this->_parser)) {
  308.             return false;
  309.         }
  310.         $length strlen($raw);
  311.         while (is_array($parsed $this->_reduce($raw))) {
  312.             list($raw$unmatched$matched$mode$parsed;
  313.             if ($this->_dispatchTokens($unmatched$matched$mode)) {
  314.                 return false;
  315.             }
  316.             if ($raw === ''{
  317.                 return true;
  318.             }
  319.             if (strlen($raw== $length{
  320.                 return false;
  321.             }
  322.             $length strlen($raw);
  323.         }
  324.         if ($parsed{
  325.             return false;
  326.         }
  327.         return $this->_invokeParser($rawLEXER_UNMATCHED);
  328.     }
  329.     
  330.     /**
  331.      *    Sends the matched token and any leading unmatched
  332.      *    text to the parser changing the lexer to a new
  333.      *    mode if one is listed.
  334.      *    @param string $unmatched    Unmatched leading portion.
  335.      *    @param string $matched      Actual token match.
  336.      *    @param string $mode         Mode after match. A boolean
  337.      *                                 false mode causes no change.
  338.      *    @return boolean             False if there was any error
  339.      *                                 from the parser.
  340.      *    @access private
  341.      */
  342.     function _dispatchTokens($unmatched$matched$mode false{
  343.         if ($this->_invokeParser($unmatchedLEXER_UNMATCHED)) {
  344.             return false;
  345.         }
  346.         if (is_bool($mode)) {
  347.             return $this->_invokeParser($matchedLEXER_MATCHED);
  348.         }
  349.         if ($this->_isModeEnd($mode)) {
  350.             if ($this->_invokeParser($matchedLEXER_EXIT)) {
  351.                 return false;
  352.             }
  353.             return $this->_mode->leave();
  354.         }
  355.         if ($this->_isSpecialMode($mode)) {
  356.             $this->_mode->enter($this->_decodeSpecial($mode));
  357.             if ($this->_invokeParser($matchedLEXER_SPECIAL)) {
  358.                 return false;
  359.             }
  360.             return $this->_mode->leave();
  361.         }
  362.         $this->_mode->enter($mode);
  363.         return $this->_invokeParser($matchedLEXER_ENTER);
  364.     }
  365.     
  366.     /**
  367.      *    Tests to see if the new mode is actually to leave
  368.      *    the current mode and pop an item from the matching
  369.      *    mode stack.
  370.      *    @param string $mode    Mode to test.
  371.      *    @return boolean        True if this is the exit mode.
  372.      *    @access private
  373.      */
  374.     function _isModeEnd($mode{
  375.         return ($mode === "__exit");
  376.     }
  377.     
  378.     /**
  379.      *    Test to see if the mode is one where this mode
  380.      *    is entered for this token only and automatically
  381.      *    leaves immediately afterwoods.
  382.      *    @param string $mode    Mode to test.
  383.      *    @return boolean        True if this is the exit mode.
  384.      *    @access private
  385.      */
  386.     function _isSpecialMode($mode{
  387.         return (strncmp($mode"_"1== 0);
  388.     }
  389.     
  390.     /**
  391.      *    Strips the magic underscore marking single token
  392.      *    modes.
  393.      *    @param string $mode    Mode to decode.
  394.      *    @return string         Underlying mode name.
  395.      *    @access private
  396.      */
  397.     function _decodeSpecial($mode{
  398.         return substr($mode1);
  399.     }
  400.     
  401.     /**
  402.      *    Calls the parser method named after the current
  403.      *    mode. Empty content will be ignored. The lexer
  404.      *    has a parser handler for each mode in the lexer.
  405.      *    @param string $content        Text parsed.
  406.      *    @param boolean $is_match      Token is recognised rather
  407.      *                                   than unparsed data.
  408.      *    @access private
  409.      */
  410.     function _invokeParser($content$is_match{
  411.         if (($content === ''|| ($content === false)) {
  412.             return true;
  413.         }
  414.         $handler $this->_mode_handlers[$this->_mode->getCurrent()];
  415.         return $this->_parser->$handler($content$is_match);
  416.     }
  417.     
  418.     /**
  419.      *    Tries to match a chunk of text and if successful
  420.      *    removes the recognised chunk and any leading
  421.      *    unparsed data. Empty strings will not be matched.
  422.      *    @param string $raw         The subject to parse. This is the
  423.      *                                content that will be eaten.
  424.      *    @return array/boolean      Three item list of unparsed
  425.      *                                content followed by the
  426.      *                                recognised token and finally the
  427.      *                                action the parser is to take.
  428.      *                                True if no match, false if there
  429.      *                                is a parsing error.
  430.      *    @access private
  431.      */
  432.     function _reduce($raw{
  433.         if ($action $this->_regexes[$this->_mode->getCurrent()]->match($raw$match)) {
  434.             $unparsed_character_count strpos($raw$match);
  435.             $unparsed substr($raw0$unparsed_character_count);
  436.             $raw substr($raw$unparsed_character_count strlen($match));
  437.             return array($raw$unparsed$match$action);
  438.         }
  439.         return true;
  440.     }
  441. }
  442.  
  443. /**
  444.  *    Breaks HTML into SAX events.
  445.  *    @package SimpleTest
  446.  *    @subpackage WebTester
  447.  */
  448. class SimpleHtmlLexer extends SimpleLexer {
  449.     
  450.     /**
  451.      *    Sets up the lexer with case insensitive matching
  452.      *    and adds the HTML handlers.
  453.      *    @param SimpleSaxParser $parser  Handling strategy by
  454.      *                                     reference.
  455.      *    @access public
  456.      */
  457.     function SimpleHtmlLexer(&$parser{
  458.         $this->SimpleLexer($parser'text');
  459.         $this->mapHandler('text''acceptTextToken');
  460.         $this->_addSkipping();
  461.         foreach ($this->_getParsedTags(as $tag{
  462.             $this->_addTag($tag);
  463.         }
  464.         $this->_addInTagTokens();
  465.     }
  466.     
  467.     /**
  468.      *    List of parsed tags. Others are ignored.
  469.      *    @return array        List of searched for tags.
  470.      *    @access private
  471.      */
  472.     function _getParsedTags({
  473.         return array('a''base''title''form''input''button''textarea''select',
  474.                 'option''frameset''frame''label');
  475.     }
  476.     
  477.     /**
  478.      *    The lexer has to skip certain sections such
  479.      *    as server code, client code and styles.
  480.      *    @access private
  481.      */
  482.     function _addSkipping({
  483.         $this->mapHandler('css''ignore');
  484.         $this->addEntryPattern('<style''text''css');
  485.         $this->addExitPattern('</style>''css');
  486.         $this->mapHandler('js''ignore');
  487.         $this->addEntryPattern('<script''text''js');
  488.         $this->addExitPattern('</script>''js');
  489.         $this->mapHandler('comment''ignore');
  490.         $this->addEntryPattern('<!--''text''comment');
  491.         $this->addExitPattern('-->''comment');
  492.     }
  493.     
  494.     /**
  495.      *    Pattern matches to start and end a tag.
  496.      *    @param string $tag          Name of tag to scan for.
  497.      *    @access private
  498.      */
  499.     function _addTag($tag{
  500.         $this->addSpecialPattern("</$tag>"'text''acceptEndToken');
  501.         $this->addEntryPattern("<$tag"'text''tag');
  502.     }
  503.     
  504.     /**
  505.      *    Pattern matches to parse the inside of a tag
  506.      *    including the attributes and their quoting.
  507.      *    @access private
  508.      */
  509.     function _addInTagTokens({
  510.         $this->mapHandler('tag''acceptStartToken');
  511.         $this->addSpecialPattern('\s+''tag''ignore');
  512.         $this->_addAttributeTokens();
  513.         $this->addExitPattern('/>''tag');
  514.         $this->addExitPattern('>''tag');
  515.     }
  516.     
  517.     /**
  518.      *    Matches attributes that are either single quoted,
  519.      *    double quoted or unquoted.
  520.      *    @access private
  521.      */
  522.     function _addAttributeTokens({
  523.         $this->mapHandler('dq_attribute''acceptAttributeToken');
  524.         $this->addEntryPattern('=\s*"''tag''dq_attribute');
  525.         $this->addPattern("\\\\\""'dq_attribute');
  526.         $this->addExitPattern('"''dq_attribute');
  527.         $this->mapHandler('sq_attribute''acceptAttributeToken');
  528.         $this->addEntryPattern("=\s*'"'tag''sq_attribute');
  529.         $this->addPattern("\\\\'"'sq_attribute');
  530.         $this->addExitPattern("'"'sq_attribute');
  531.         $this->mapHandler('uq_attribute''acceptAttributeToken');
  532.         $this->addSpecialPattern('=\s*[^>\s]*''tag''uq_attribute');
  533.     }
  534. }
  535.  
  536. /**
  537.  *    Converts HTML tokens into selected SAX events.
  538.  *    @package SimpleTest
  539.  *    @subpackage WebTester
  540.  */
  541.     var $_lexer;
  542.     var $_listener;
  543.     var $_tag;
  544.     var $_attributes;
  545.     var $_current_attribute;
  546.     
  547.     /**
  548.      *    Sets the listener.
  549.      *    @param SimpleSaxListener $listener    SAX event handler.
  550.      *    @access public
  551.      */
  552.     function SimpleHtmlSaxParser(&$listener{
  553.         $this->_listener = &$listener;
  554.         $this->_lexer = &$this->createLexer($this);
  555.         $this->_tag = '';
  556.         $this->_attributes = array();
  557.         $this->_current_attribute = '';
  558.     }
  559.     
  560.     /**
  561.      *    Runs the content through the lexer which
  562.      *    should call back to the acceptors.
  563.      *    @param string $raw      Page text to parse.
  564.      *    @return boolean         False if parse error.
  565.      *    @access public
  566.      */
  567.     function parse($raw{
  568.         return $this->_lexer->parse($raw);
  569.     }
  570.     
  571.     /**
  572.      *    Sets up the matching lexer. Starts in 'text' mode.
  573.      *    @param SimpleSaxParser $parser    Event generator, usually $self.
  574.      *    @return SimpleLexer               Lexer suitable for this parser.
  575.      *    @access public
  576.      *    @static
  577.      */
  578.     function &createLexer(&$parser{
  579.         $lexer &new SimpleHtmlLexer($parser);
  580.         return $lexer;
  581.     }
  582.     
  583.     /**
  584.      *    Accepts a token from the tag mode. If the
  585.      *    starting element completes then the element
  586.      *    is dispatched and the current attributes
  587.      *    set back to empty. The element or attribute
  588.      *    name is converted to lower case.
  589.      *    @param string $token     Incoming characters.
  590.      *    @param integer $event    Lexer event type.
  591.      *    @return boolean          False if parse error.
  592.      *    @access public
  593.      */
  594.     function acceptStartToken($token$event{
  595.         if ($event == LEXER_ENTER{
  596.             $this->_tag = strtolower(substr($token1));
  597.             return true;
  598.         }
  599.         if ($event == LEXER_EXIT{
  600.             $success $this->_listener->startElement(
  601.                     $this->_tag,
  602.                     $this->_attributes);
  603.             $this->_tag = '';
  604.             $this->_attributes = array();
  605.             return $success;
  606.         }
  607.         if ($token != '='{
  608.             $this->_current_attribute = strtolower(SimpleHtmlSaxParser::decodeHtml($token));
  609.             $this->_attributes[$this->_current_attribute'';
  610.         }
  611.         return true;
  612.     }
  613.     
  614.     /**
  615.      *    Accepts a token from the end tag mode.
  616.      *    The element name is converted to lower case.
  617.      *    @param string $token     Incoming characters.
  618.      *    @param integer $event    Lexer event type.
  619.      *    @return boolean          False if parse error.
  620.      *    @access public
  621.      */
  622.     function acceptEndToken($token$event{
  623.         if (preg_match('/<\/(.*)>/'$token$matches)) {
  624.             return false;
  625.         }
  626.         return $this->_listener->endElement(strtolower($matches[1]));
  627.     }
  628.     
  629.     /**
  630.      *    Part of the tag data.
  631.      *    @param string $token     Incoming characters.
  632.      *    @param integer $event    Lexer event type.
  633.      *    @return boolean          False if parse error.
  634.      *    @access public
  635.      */
  636.     function acceptAttributeToken($token$event{
  637.         if ($this->_current_attribute{
  638.             if ($event == LEXER_UNMATCHED{
  639.                 $this->_attributes[$this->_current_attribute.=
  640.                         SimpleHtmlSaxParser::decodeHtml($token);
  641.             }
  642.             if ($event == LEXER_SPECIAL{
  643.                 $this->_attributes[$this->_current_attribute.=
  644.                         preg_replace('/^=\s*/' ''SimpleHtmlSaxParser::decodeHtml($token));
  645.             }
  646.         }
  647.         return true;
  648.     }
  649.     
  650.     /**
  651.      *    A character entity.
  652.      *    @param string $token    Incoming characters.
  653.      *    @param integer $event   Lexer event type.
  654.      *    @return boolean         False if parse error.
  655.      *    @access public
  656.      */
  657.     function acceptEntityToken($token$event{
  658.     }
  659.     
  660.     /**
  661.      *    Character data between tags regarded as
  662.      *    important.
  663.      *    @param string $token     Incoming characters.
  664.      *    @param integer $event    Lexer event type.
  665.      *    @return boolean          False if parse error.
  666.      *    @access public
  667.      */
  668.     function acceptTextToken($token$event{
  669.         return $this->_listener->addContent($token);
  670.     }
  671.     
  672.     /**
  673.      *    Incoming data to be ignored.
  674.      *    @param string $token     Incoming characters.
  675.      *    @param integer $event    Lexer event type.
  676.      *    @return boolean          False if parse error.
  677.      *    @access public
  678.      */
  679.     function ignore($token$event{
  680.         return true;
  681.     }
  682.     
  683.     /**
  684.      *    Decodes any HTML entities.
  685.      *    @param string $html    Incoming HTML.
  686.      *    @return string         Outgoing plain text.
  687.      *    @access public
  688.      *    @static
  689.      */
  690.     function decodeHtml($html{
  691.         return html_entity_decode($htmlENT_QUOTES);
  692.     }
  693.     
  694.     /**
  695.      *    Turns HTML into text browser visible text. Images
  696.      *    are converted to their alt text and tags are supressed.
  697.      *    Entities are converted to their visible representation.
  698.      *    @param string $html        HTML to convert.
  699.      *    @return string             Plain text.
  700.      *    @access public
  701.      *    @static
  702.      */
  703.     function normalise($html{
  704.         $text preg_replace('|<!--.*?-->|'''$html);
  705.         $text preg_replace('|<script[^>]*>.*?</script>|'''$text);
  706.         $text preg_replace('|<img[^>]*alt\s*=\s*"([^"]*)"[^>]*>|'' \1 '$text);
  707.         $text preg_replace('|<img[^>]*alt\s*=\s*\'([^\']*)\'[^>]*>|'' \1 '$text);
  708.         $text preg_replace('|<img[^>]*alt\s*=\s*([a-zA-Z_]+)[^>]*>|'' \1 '$text);
  709.         $text preg_replace('|<[^>]*>|'''$text);
  710.         $text SimpleHtmlSaxParser::decodeHtml($text);
  711.         $text preg_replace('|\s+|'' '$text);
  712.         return trim(trim($text)"\xA0");        // TODO: The \xAO is a &nbsp;. Add a test for this.
  713.     }
  714. }
  715.  
  716. /**
  717.  *    SAX event handler.
  718.  *    @package SimpleTest
  719.  *    @subpackage WebTester
  720.  *    @abstract
  721.  */
  722.     
  723.     /**
  724.      *    Sets the document to write to.
  725.      *    @access public
  726.      */
  727.     function SimpleSaxListener({
  728.     }
  729.     
  730.     /**
  731.      *    Start of element event.
  732.      *    @param string $name        Element name.
  733.      *    @param hash $attributes    Name value pairs.
  734.      *                                Attributes without content
  735.      *                                are marked as true.
  736.      *    @return boolean            False on parse error.
  737.      *    @access public
  738.      */
  739.     function startElement($name$attributes{
  740.     }
  741.     
  742.     /**
  743.      *    End of element event.
  744.      *    @param string $name        Element name.
  745.      *    @return boolean            False on parse error.
  746.      *    @access public
  747.      */
  748.     function endElement($name{
  749.     }
  750.     
  751.     /**
  752.      *    Unparsed, but relevant data.
  753.      *    @param string $text        May include unparsed tags.
  754.      *    @return boolean            False on parse error.
  755.      *    @access public
  756.      */
  757.     function addContent($text{
  758.     }
  759. }
  760. ?>

Documentation generated on Sun, 04 May 2008 09:21:55 -0500 by phpDocumentor 1.3.0