Source for file xml.php

Documentation is available at xml.php

  1. <?php
  2. /**
  3.  *  base include file for SimpleTest
  4.  *  @package    SimpleTest
  5.  *  @subpackage UnitTester
  6.  *  @version    $Id: xml.php 1787 2008-04-26 20:35:39Z pp11 $
  7.  */
  8.  
  9. /**#@+
  10.  *  include other SimpleTest class files
  11.  */
  12. require_once(dirname(__FILE__'/scorer.php');
  13. /**#@-*/
  14.  
  15. /**
  16.  *    Creates the XML needed for remote communication
  17.  *    by SimpleTest.
  18.  *    @package SimpleTest
  19.  *    @subpackage UnitTester
  20.  */
  21. class XmlReporter extends SimpleReporter {
  22.     private $indent;
  23.     private $namespace;
  24.  
  25.     /**
  26.      *    Sets up indentation and namespace.
  27.      *    @param string $namespace        Namespace to add to each tag.
  28.      *    @param string $indent           Indenting to add on each nesting.
  29.      *    @access public
  30.      */
  31.     function __construct($namespace false$indent '  '{
  32.         parent::__construct();
  33.         $this->namespace ($namespace $namespace ':' '');
  34.         $this->indent $indent;
  35.     }
  36.  
  37.     /**
  38.      *    Calculates the pretty printing indent level
  39.      *    from the current level of nesting.
  40.      *    @param integer $offset  Extra indenting level.
  41.      *    @return string          Leading space.
  42.      *    @access protected
  43.      */
  44.     protected function getIndent($offset 0{
  45.         return str_repeat(
  46.                 $this->indent,
  47.                 count($this->getTestList()) $offset);
  48.     }
  49.  
  50.     /**
  51.      *    Converts character string to parsed XML
  52.      *    entities string.
  53.      *    @param string text        Unparsed character data.
  54.      *    @return string            Parsed character data.
  55.      *    @access public
  56.      */
  57.     function toParsedXml($text{
  58.         return str_replace(
  59.                 array('&''<''>''"''\''),
  60.                 array('&amp;''&lt;''&gt;''&quot;''&apos;'),
  61.                 $text);
  62.     }
  63.  
  64.     /**
  65.      *    Paints the start of a group test.
  66.      *    @param string $test_name   Name of test that is starting.
  67.      *    @param integer $size       Number of test cases starting.
  68.      *    @access public
  69.      */
  70.     function paintGroupStart($test_name$size{
  71.         parent::paintGroupStart($test_name$size);
  72.         print $this->getIndent();
  73.         print "<" $this->namespace "group size=\"$size\">\n";
  74.         print $this->getIndent(1);
  75.         print "<" $this->namespace "name>" .
  76.                 $this->toParsedXml($test_name.
  77.                 "</" $this->namespace "name>\n";
  78.     }
  79.  
  80.     /**
  81.      *    Paints the end of a group test.
  82.      *    @param string $test_name   Name of test that is ending.
  83.      *    @access public
  84.      */
  85.     function paintGroupEnd($test_name{
  86.         print $this->getIndent();
  87.         print "</" $this->namespace "group>\n";
  88.         parent::paintGroupEnd($test_name);
  89.     }
  90.  
  91.     /**
  92.      *    Paints the start of a test case.
  93.      *    @param string $test_name   Name of test that is starting.
  94.      *    @access public
  95.      */
  96.     function paintCaseStart($test_name{
  97.         parent::paintCaseStart($test_name);
  98.         print $this->getIndent();
  99.         print "<" $this->namespace "case>\n";
  100.         print $this->getIndent(1);
  101.         print "<" $this->namespace "name>" .
  102.                 $this->toParsedXml($test_name.
  103.                 "</" $this->namespace "name>\n";
  104.     }
  105.  
  106.     /**
  107.      *    Paints the end of a test case.
  108.      *    @param string $test_name   Name of test that is ending.
  109.      *    @access public
  110.      */
  111.     function paintCaseEnd($test_name{
  112.         print $this->getIndent();
  113.         print "</" $this->namespace "case>\n";
  114.         parent::paintCaseEnd($test_name);
  115.     }
  116.  
  117.     /**
  118.      *    Paints the start of a test method.
  119.      *    @param string $test_name   Name of test that is starting.
  120.      *    @access public
  121.      */
  122.     function paintMethodStart($test_name{
  123.         parent::paintMethodStart($test_name);
  124.         print $this->getIndent();
  125.         print "<" $this->namespace "test>\n";
  126.         print $this->getIndent(1);
  127.         print "<" $this->namespace "name>" .
  128.                 $this->toParsedXml($test_name.
  129.                 "</" $this->namespace "name>\n";
  130.     }
  131.  
  132.     /**
  133.      *    Paints the end of a test method.
  134.      *    @param string $test_name   Name of test that is ending.
  135.      *    @param integer $progress   Number of test cases ending.
  136.      *    @access public
  137.      */
  138.     function paintMethodEnd($test_name{
  139.         print $this->getIndent();
  140.         print "</" $this->namespace "test>\n";
  141.         parent::paintMethodEnd($test_name);
  142.     }
  143.  
  144.     /**
  145.      *    Paints pass as XML.
  146.      *    @param string $message        Message to encode.
  147.      *    @access public
  148.      */
  149.     function paintPass($message{
  150.         parent::paintPass($message);
  151.         print $this->getIndent(1);
  152.         print "<" $this->namespace "pass>";
  153.         print $this->toParsedXml($message);
  154.         print "</" $this->namespace "pass>\n";
  155.     }
  156.  
  157.     /**
  158.      *    Paints failure as XML.
  159.      *    @param string $message        Message to encode.
  160.      *    @access public
  161.      */
  162.     function paintFail($message{
  163.         parent::paintFail($message);
  164.         print $this->getIndent(1);
  165.         print "<" $this->namespace "fail>";
  166.         print $this->toParsedXml($message);
  167.         print "</" $this->namespace "fail>\n";
  168.     }
  169.  
  170.     /**
  171.      *    Paints error as XML.
  172.      *    @param string $message        Message to encode.
  173.      *    @access public
  174.      */
  175.     function paintError($message{
  176.         parent::paintError($message);
  177.         print $this->getIndent(1);
  178.         print "<" $this->namespace "exception>";
  179.         print $this->toParsedXml($message);
  180.         print "</" $this->namespace "exception>\n";
  181.     }
  182.  
  183.     /**
  184.      *    Paints exception as XML.
  185.      *    @param Exception $exception    Exception to encode.
  186.      *    @access public
  187.      */
  188.     function paintException($exception{
  189.         parent::paintException($exception);
  190.         print $this->getIndent(1);
  191.         print "<" $this->namespace "exception>";
  192.         $message 'Unexpected exception of type [' get_class($exception.
  193.                 '] with message ['$exception->getMessage(.
  194.                 '] in ['$exception->getFile(.
  195.                 ' line ' $exception->getLine(']';
  196.         print $this->toParsedXml($message);
  197.         print "</" $this->namespace "exception>\n";
  198.     }
  199.  
  200.     /**
  201.      *    Paints the skipping message and tag.
  202.      *    @param string $message        Text to display in skip tag.
  203.      *    @access public
  204.      */
  205.     function paintSkip($message{
  206.         parent::paintSkip($message);
  207.         print $this->getIndent(1);
  208.         print "<" $this->namespace "skip>";
  209.         print $this->toParsedXml($message);
  210.         print "</" $this->namespace "skip>\n";
  211.     }
  212.  
  213.     /**
  214.      *    Paints a simple supplementary message.
  215.      *    @param string $message        Text to display.
  216.      *    @access public
  217.      */
  218.     function paintMessage($message{
  219.         parent::paintMessage($message);
  220.         print $this->getIndent(1);
  221.         print "<" $this->namespace "message>";
  222.         print $this->toParsedXml($message);
  223.         print "</" $this->namespace "message>\n";
  224.     }
  225.  
  226.     /**
  227.      *    Paints a formatted ASCII message such as a
  228.      *    privateiable dump.
  229.      *    @param string $message        Text to display.
  230.      *    @access public
  231.      */
  232.     function paintFormattedMessage($message{
  233.         parent::paintFormattedMessage($message);
  234.         print $this->getIndent(1);
  235.         print "<" $this->namespace "formatted>";
  236.         print "<![CDATA[$message]]>";
  237.         print "</" $this->namespace "formatted>\n";
  238.     }
  239.  
  240.     /**
  241.      *    Serialises the event object.
  242.      *    @param string $type        Event type as text.
  243.      *    @param mixed $payload      Message or object.
  244.      *    @access public
  245.      */
  246.     function paintSignal($type$payload{
  247.         parent::paintSignal($type$payload);
  248.         print $this->getIndent(1);
  249.         print "<" $this->namespace "signal type=\"$type\">";
  250.         print "<![CDATA[" serialize($payload"]]>";
  251.         print "</" $this->namespace "signal>\n";
  252.     }
  253.  
  254.     /**
  255.      *    Paints the test document header.
  256.      *    @param string $test_name     First test top level
  257.      *                                  to start.
  258.      *    @access public
  259.      *    @abstract
  260.      */
  261.     function paintHeader($test_name{
  262.         if (SimpleReporter::inCli()) {
  263.             header('Content-type: text/xml');
  264.         }
  265.         print "<?xml version=\"1.0\"";
  266.         if ($this->namespace{
  267.             print " xmlns:" $this->namespace .
  268.                     "=\"www.lastcraft.com/SimpleTest/Beta3/Report\"";
  269.         }
  270.         print "?>\n";
  271.         print "<" $this->namespace "run>\n";
  272.     }
  273.  
  274.     /**
  275.      *    Paints the test document footer.
  276.      *    @param string $test_name        The top level test.
  277.      *    @access public
  278.      *    @abstract
  279.      */
  280.     function paintFooter($test_name{
  281.         print "</" $this->namespace "run>\n";
  282.     }
  283. }
  284.  
  285. /**
  286.  *    Accumulator for incoming tag. Holds the
  287.  *    incoming test structure information for
  288.  *    later dispatch to the reporter.
  289.  *    @package SimpleTest
  290.  *    @subpackage UnitTester
  291.  */
  292. class NestingXmlTag {
  293.     private $name;
  294.     private $attributes;
  295.  
  296.     /**
  297.      *    Sets the basic test information except
  298.      *    the name.
  299.      *    @param hash $attributes   Name value pairs.
  300.      *    @access public
  301.      */
  302.     function NestingXmlTag($attributes{
  303.         $this->name false;
  304.         $this->attributes $attributes;
  305.     }
  306.  
  307.     /**
  308.      *    Sets the test case/method name.
  309.      *    @param string $name        Name of test.
  310.      *    @access public
  311.      */
  312.     function setName($name{
  313.         $this->name $name;
  314.     }
  315.  
  316.     /**
  317.      *    Accessor for name.
  318.      *    @return string        Name of test.
  319.      *    @access public
  320.      */
  321.     function getName({
  322.         return $this->name;
  323.     }
  324.  
  325.     /**
  326.      *    Accessor for attributes.
  327.      *    @return hash        All attributes.
  328.      *    @access protected
  329.      */
  330.     protected function getAttributes({
  331.         return $this->attributes;
  332.     }
  333. }
  334.  
  335. /**
  336.  *    Accumulator for incoming method tag. Holds the
  337.  *    incoming test structure information for
  338.  *    later dispatch to the reporter.
  339.  *    @package SimpleTest
  340.  *    @subpackage UnitTester
  341.  */
  342. class NestingMethodTag extends NestingXmlTag {
  343.  
  344.     /**
  345.      *    Sets the basic test information except
  346.      *    the name.
  347.      *    @param hash $attributes   Name value pairs.
  348.      *    @access public
  349.      */
  350.     function NestingMethodTag($attributes{
  351.         $this->NestingXmlTag($attributes);
  352.     }
  353.  
  354.     /**
  355.      *    Signals the appropriate start event on the
  356.      *    listener.
  357.      *    @param SimpleReporter $listener    Target for events.
  358.      *    @access public
  359.      */
  360.     function paintStart(&$listener{
  361.         $listener->paintMethodStart($this->getName());
  362.     }
  363.  
  364.     /**
  365.      *    Signals the appropriate end event on the
  366.      *    listener.
  367.      *    @param SimpleReporter $listener    Target for events.
  368.      *    @access public
  369.      */
  370.     function paintEnd(&$listener{
  371.         $listener->paintMethodEnd($this->getName());
  372.     }
  373. }
  374.  
  375. /**
  376.  *    Accumulator for incoming case tag. Holds the
  377.  *    incoming test structure information for
  378.  *    later dispatch to the reporter.
  379.  *    @package SimpleTest
  380.  *    @subpackage UnitTester
  381.  */
  382. class NestingCaseTag extends NestingXmlTag {
  383.  
  384.     /**
  385.      *    Sets the basic test information except
  386.      *    the name.
  387.      *    @param hash $attributes   Name value pairs.
  388.      *    @access public
  389.      */
  390.     function NestingCaseTag($attributes{
  391.         $this->NestingXmlTag($attributes);
  392.     }
  393.  
  394.     /**
  395.      *    Signals the appropriate start event on the
  396.      *    listener.
  397.      *    @param SimpleReporter $listener    Target for events.
  398.      *    @access public
  399.      */
  400.     function paintStart(&$listener{
  401.         $listener->paintCaseStart($this->getName());
  402.     }
  403.  
  404.     /**
  405.      *    Signals the appropriate end event on the
  406.      *    listener.
  407.      *    @param SimpleReporter $listener    Target for events.
  408.      *    @access public
  409.      */
  410.     function paintEnd(&$listener{
  411.         $listener->paintCaseEnd($this->getName());
  412.     }
  413. }
  414.  
  415. /**
  416.  *    Accumulator for incoming group tag. Holds the
  417.  *    incoming test structure information for
  418.  *    later dispatch to the reporter.
  419.  *    @package SimpleTest
  420.  *    @subpackage UnitTester
  421.  */
  422. class NestingGroupTag extends NestingXmlTag {
  423.  
  424.     /**
  425.      *    Sets the basic test information except
  426.      *    the name.
  427.      *    @param hash $attributes   Name value pairs.
  428.      *    @access public
  429.      */
  430.     function NestingGroupTag($attributes{
  431.         $this->NestingXmlTag($attributes);
  432.     }
  433.  
  434.     /**
  435.      *    Signals the appropriate start event on the
  436.      *    listener.
  437.      *    @param SimpleReporter $listener    Target for events.
  438.      *    @access public
  439.      */
  440.     function paintStart(&$listener{
  441.         $listener->paintGroupStart($this->getName()$this->getSize());
  442.     }
  443.  
  444.     /**
  445.      *    Signals the appropriate end event on the
  446.      *    listener.
  447.      *    @param SimpleReporter $listener    Target for events.
  448.      *    @access public
  449.      */
  450.     function paintEnd(&$listener{
  451.         $listener->paintGroupEnd($this->getName());
  452.     }
  453.  
  454.     /**
  455.      *    The size in the attributes.
  456.      *    @return integer     Value of size attribute or zero.
  457.      *    @access public
  458.      */
  459.     function getSize({
  460.         $attributes $this->getAttributes();
  461.         if (isset($attributes['SIZE'])) {
  462.             return (integer)$attributes['SIZE'];
  463.         }
  464.         return 0;
  465.     }
  466. }
  467.  
  468. /**
  469.  *    Parser for importing the output of the XmlReporter.
  470.  *    Dispatches that output to another reporter.
  471.  *    @package SimpleTest
  472.  *    @subpackage UnitTester
  473.  */
  474.     private $listener;
  475.     private $expat;
  476.     private $tag_stack;
  477.     private $in_content_tag;
  478.     private $content;
  479.     private $attributes;
  480.  
  481.     /**
  482.      *    Loads a listener with the SimpleReporter
  483.      *    interface.
  484.      *    @param SimpleReporter $listener   Listener of tag events.
  485.      *    @access public
  486.      */
  487.     function SimpleTestXmlParser(&$listener{
  488.         $this->listener &$listener;
  489.         $this->expat &$this->createParser();
  490.         $this->tag_stack array();
  491.         $this->in_content_tag false;
  492.         $this->content '';
  493.         $this->attributes array();
  494.     }
  495.  
  496.     /**
  497.      *    Parses a block of XML sending the results to
  498.      *    the listener.
  499.      *    @param string $chunk        Block of text to read.
  500.      *    @return boolean             True if valid XML.
  501.      *    @access public
  502.      */
  503.     function parse($chunk{
  504.         if (xml_parse($this->expat$chunk)) {
  505.             trigger_error('XML parse error with ' .
  506.                     xml_error_string(xml_get_error_code($this->expat)));
  507.             return false;
  508.         }
  509.         return true;
  510.     }
  511.  
  512.     /**
  513.      *    Sets up expat as the XML parser.
  514.      *    @return resource        Expat handle.
  515.      *    @access protected
  516.      */
  517.     protected function &createParser({
  518.         $expat xml_parser_create();
  519.         xml_set_object($expat$this);
  520.         xml_set_element_handler($expat'startElement''endElement');
  521.         xml_set_character_data_handler($expat'addContent');
  522.         xml_set_default_handler($expat'defaultContent');
  523.         return $expat;
  524.     }
  525.  
  526.     /**
  527.      *    Opens a new test nesting level.
  528.      *    @return NestedXmlTag     The group, case or method tag
  529.      *                              to start.
  530.      *    @access private
  531.      */
  532.     protected function pushNestingTag($nested{
  533.         array_unshift($this->tag_stack$nested);
  534.     }
  535.  
  536.     /**
  537.      *    Accessor for current test structure tag.
  538.      *    @return NestedXmlTag     The group, case or method tag
  539.      *                              being parsed.
  540.      *    @access private
  541.      */
  542.     protected function &getCurrentNestingTag({
  543.         return $this->tag_stack[0];
  544.     }
  545.  
  546.     /**
  547.      *    Ends a nesting tag.
  548.      *    @return NestedXmlTag     The group, case or method tag
  549.      *                              just finished.
  550.      *    @access private
  551.      */
  552.     protected function popNestingTag({
  553.         return array_shift($this->tag_stack);
  554.     }
  555.  
  556.     /**
  557.      *    Test if tag is a leaf node with only text content.
  558.      *    @param string $tag        XML tag name.
  559.      *    @return @boolean          True if leaf, false if nesting.
  560.      *    @private
  561.      */
  562.     protected function isLeaf($tag{
  563.         return in_array($tagarray(
  564.                 'NAME''PASS''FAIL''EXCEPTION''SKIP''MESSAGE''FORMATTED''SIGNAL'));
  565.     }
  566.  
  567.     /**
  568.      *    Handler for start of event element.
  569.      *    @param resource $expat     Parser handle.
  570.      *    @param string $tag         Element name.
  571.      *    @param hash $attributes    Name value pairs.
  572.      *                                Attributes without content
  573.      *                                are marked as true.
  574.      *    @access protected
  575.      */
  576.     protected function startElement($expat$tag$attributes{
  577.         $this->attributes $attributes;
  578.         if ($tag == 'GROUP'{
  579.             $this->pushNestingTag(new NestingGroupTag($attributes));
  580.         elseif ($tag == 'CASE'{
  581.             $this->pushNestingTag(new NestingCaseTag($attributes));
  582.         elseif ($tag == 'TEST'{
  583.             $this->pushNestingTag(new NestingMethodTag($attributes));
  584.         elseif ($this->isLeaf($tag)) {
  585.             $this->in_content_tag true;
  586.             $this->content '';
  587.         }
  588.     }
  589.  
  590.     /**
  591.      *    End of element event.
  592.      *    @param resource $expat     Parser handle.
  593.      *    @param string $tag         Element name.
  594.      *    @access protected
  595.      */
  596.     protected function endElement($expat$tag{
  597.         $this->in_content_tag false;
  598.         if (in_array($tagarray('GROUP''CASE''TEST'))) {
  599.             $nesting_tag $this->popNestingTag();
  600.             $nesting_tag->paintEnd($this->listener);
  601.         elseif ($tag == 'NAME'{
  602.             $nesting_tag &$this->getCurrentNestingTag();
  603.             $nesting_tag->setName($this->content);
  604.             $nesting_tag->paintStart($this->listener);
  605.         elseif ($tag == 'PASS'{
  606.             $this->listener->paintPass($this->content);
  607.         elseif ($tag == 'FAIL'{
  608.             $this->listener->paintFail($this->content);
  609.         elseif ($tag == 'EXCEPTION'{
  610.             $this->listener->paintError($this->content);
  611.         elseif ($tag == 'SKIP'{
  612.             $this->listener->paintSkip($this->content);
  613.         elseif ($tag == 'SIGNAL'{
  614.             $this->listener->paintSignal(
  615.                     $this->attributes['TYPE'],
  616.                     unserialize($this->content));
  617.         elseif ($tag == 'MESSAGE'{
  618.             $this->listener->paintMessage($this->content);
  619.         elseif ($tag == 'FORMATTED'{
  620.             $this->listener->paintFormattedMessage($this->content);
  621.         }
  622.     }
  623.  
  624.     /**
  625.      *    Content between start and end elements.
  626.      *    @param resource $expat     Parser handle.
  627.      *    @param string $text        Usually output messages.
  628.      *    @access protected
  629.      */
  630.     protected function addContent($expat$text{
  631.         if ($this->in_content_tag{
  632.             $this->content .= $text;
  633.         }
  634.         return true;
  635.     }
  636.  
  637.     /**
  638.      *    XML and Doctype handler. Discards all such content.
  639.      *    @param resource $expat     Parser handle.
  640.      *    @param string $default     Text of default content.
  641.      *    @access protected
  642.      */
  643.     protected function defaultContent($expat$default{
  644.     }
  645. }
  646. ?>

Documentation generated on Sun, 31 Oct 2010 16:33:11 -0500 by phpDocumentor 1.4.3