Testing_DocTest
[ class tree: Testing_DocTest ] [ index: Testing_DocTest ] [ all elements ]

Source for file Default.php

Documentation is available at Default.php

  1. <?php
  2.  
  3. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  4.  
  5. /**
  6.  * This file is part of the PEAR Testing_DocTest package.
  7.  *
  8.  * PHP version 5
  9.  *
  10.  * LICENSE: This source file is subject to the MIT license that is available
  11.  * through the world-wide-web at the following URI:
  12.  * http://opensource.org/licenses/mit-license.php
  13.  *
  14.  * @category  Testing
  15.  * @package   Testing_DocTest
  16.  * @author    David JEAN LOUIS <izimobil@gmail.com>
  17.  * @copyright 2008 David JEAN LOUIS
  18.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  19.  * @version   CVS: $Id: Default.php,v 1.3 2009/06/25 09:10:06 izi Exp $
  20.  * @link      http://pear.php.net/package/Testing_DocTest
  21.  * @since     File available since release 0.1.0
  22.  * @filesource
  23.  */
  24.  
  25. /**
  26.  * Required file.
  27.  */
  28. require_once 'Testing/DocTest/ParserInterface.php';
  29. require_once 'Testing/DocTest/TestSuite.php';
  30. require_once 'Testing/DocTest/TestCase.php';
  31.  
  32. /**
  33.  * DocTest Parser default class.
  34.  * Important note: this class will be refactored soon so do not rely on it yet
  35.  * if you want to subclass or customize Testing_DocTest.
  36.  *
  37.  * @category  Testing
  38.  * @package   Testing_DocTest
  39.  * @author    David JEAN LOUIS <izimobil@gmail.com>
  40.  * @copyright 2008 David JEAN LOUIS
  41.  * @license   http://opensource.org/licenses/mit-license.php MIT License
  42.  * @version   Release: 0.3.1
  43.  * @link      http://pear.php.net/package/Testing_DocTest
  44.  * @since     Class available since release 0.1.0
  45.  */
  46. class Testing_DocTest_Parser_Default implements Testing_DocTest_ParserInterface
  47. {
  48.     // doctest syntax prefix {{{
  49.  
  50.     /**
  51.      * Doctest syntax prefix default is a standard php inline comment: '//'
  52.      */
  53.     const SYNTAX_PREFIX = '//';
  54.  
  55.     // }}}
  56.     // Keywords constants {{{
  57.  
  58.     /**
  59.      * Keyword for the name of the external doctest file
  60.      */
  61.     const KW_DOCTEST_FILE = 'test-file';
  62.     
  63.     /**
  64.      * Keyword for the name of the doctest
  65.      */
  66.     const KW_DOCTEST_NAME = 'doctest';
  67.  
  68.     /**
  69.      * Keyword for the doctest flags
  70.      */
  71.     const KW_DOCTEST_FLAGS = 'flags';
  72.  
  73.     /**
  74.      * Keyword for the skip condition
  75.      */
  76.     const KW_DOCTEST_SKIP_IF = 'skip-if';
  77.  
  78.     /**
  79.      * Keyword for the ini settings
  80.      */
  81.     const KW_DOCTEST_INI_SET = 'ini-set';
  82.  
  83.     /**
  84.      * Keyword for the doctest expected result
  85.      */
  86.     const KW_DOCTEST_EXPECTS = 'expects';
  87.  
  88.     /**
  89.      * Keyword for the doctest expected file
  90.      */
  91.     const KW_DOCTEST_EXPECTS_FILE = 'expects-file';
  92.  
  93.     /**
  94.      * Keyword for the clean part
  95.      */
  96.     const KW_DOCTEST_CLEAN = 'clean';
  97.  
  98.     // }}}
  99.     // State constants {{{
  100.  
  101.     /**
  102.      * State after parsing a doctest line.
  103.      */
  104.     const STATE_DOCTEST = 1;
  105.  
  106.     /**
  107.      * State after parsing flags.
  108.      */
  109.     const STATE_FLAGS = 2;
  110.  
  111.     /**
  112.      * State after parsing a skip condition line.
  113.      */
  114.     const STATE_SKIP_IF = 3;
  115.  
  116.     /**
  117.      * State after parsing a ini-set line.
  118.      */
  119.     const STATE_INI_SET = 4;
  120.  
  121.     /**
  122.      * State after parsing expects line.
  123.      */
  124.     const STATE_EXPECTS = 5;
  125.  
  126.     /**
  127.      * State after parsing expects-file line.
  128.      */
  129.     const STATE_EXPECTS_FILE = 6;
  130.  
  131.     /**
  132.      * State after parsing code line.
  133.      */
  134.     const STATE_CODE = 7;
  135.  
  136.     /**
  137.      * State after parsing clean line.
  138.      */
  139.     const STATE_CLEAN = 8;
  140.  
  141.     // }}}
  142.     // Properties {{{
  143.  
  144.     /**
  145.      * Current state of the parser, null or one of the STATE_*  constants.
  146.      *
  147.      * @var int $_state 
  148.      * @access private
  149.      */
  150.     private $_state = null;
  151.  
  152.     /**
  153.      * Testing_DocTest_TestCase instance.
  154.      *
  155.      * @var object $_testCase 
  156.      * @access private
  157.      */
  158.     private $_testCase = null;
  159.  
  160.     // }}}
  161.     // parse() {{{
  162.  
  163.     /**
  164.      * Parse the files passed and return an array of Testing_DocTest_TestSuite
  165.      * instances.
  166.      *
  167.      * @param array $files an array of file pathes
  168.      *
  169.      * @access public
  170.      * @return array 
  171.      */
  172.     public function parse(array $files)
  173.     {
  174.         $ret = array();
  175.         $kw  = preg_quote(self::KW_DOCTEST_NAME'/')    '|'
  176.              . preg_quote(self::KW_DOCTEST_FLAGS'/')   '|'
  177.              . preg_quote(self::KW_DOCTEST_SKIP_IF'/''|'
  178.              . preg_quote(self::KW_DOCTEST_INI_SET'/''|'
  179.              . preg_quote(self::KW_DOCTEST_CLEAN'/')   '|'
  180.              . preg_quote(self::KW_DOCTEST_EXPECTS'/''|'
  181.              . preg_quote(self::KW_DOCTEST_EXPECTS_FILE'/');
  182.         foreach ($files as $file{
  183.             $testCaseArray $this->_parseFile($file);
  184.             $suite         = false;
  185.             foreach ($testCaseArray as $testCaseData{
  186.                 // split raw code into lines
  187.                 $docblocs $this->_extractCodeBlocs($testCaseData['docComment']);
  188.                 // build our suite
  189.                 if (!empty($docblocs&& false == $suite{
  190.                     $suite       = new Testing_DocTest_TestSuite();
  191.                     $suite->name = $file
  192.                 }
  193.                 foreach ($docblocs as $docbloc{
  194.                     $this->_testCase        = new Testing_DocTest_TestCase();
  195.                     $this->_testCase->file  = $file;
  196.                     $this->_testCase->level = $testCaseData['level'];
  197.                     $this->_testCase->name  = $testCaseData['name'];
  198.                     // split string into an array of lines
  199.                     $lines preg_split('/(\n|\r\n)/'$docbloc);
  200.                     foreach ($lines as $i=>$l{
  201.                         // remove spaces and * at the beginning
  202.                         $l preg_replace('/^\s*\*\s?/'''$l);
  203.                         $p preg_quote(self::SYNTAX_PREFIX'/');
  204.                         if (preg_match("/^\s*$p\s?($kw):\s*(.*)$/"$l$m)) {
  205.                             switch ($m[1]{
  206.                             case self::KW_DOCTEST_NAME:
  207.                                 $this->_handleDoctestLine($m[2]);
  208.                                 break;
  209.                             case self::KW_DOCTEST_FLAGS:
  210.                                 $this->_handleFlagsLine($m[2]);
  211.                                 break;
  212.                             case self::KW_DOCTEST_SKIP_IF:
  213.                                 $this->_handleFlagsLine($m[2]);
  214.                                 break;
  215.                             case self::KW_DOCTEST_INI_SET:
  216.                                 $this->_handleIniSetLine($m[2]);
  217.                                 break;
  218.                             case self::KW_DOCTEST_EXPECTS:
  219.                                 $this->_handleExpectsLine($m[2]);
  220.                                 break;
  221.                             case self::KW_DOCTEST_EXPECTS_FILE:
  222.                                 $this->_handleExpectsFileLine($m[2]);
  223.                                 break;
  224.                             case self::KW_DOCTEST_CLEAN:
  225.                                 $this->_handleCleanLine($m[2]);
  226.                             }
  227.                         else if (preg_match('/^\s*'.$p.'\s?(.*)$/'$l$m)) {
  228.                             $this->_handleLineContinuation($m[1]);
  229.                         else {
  230.                             if (trim($l!= ''{
  231.                                 $this->_handleCodeLine($l);
  232.                             }
  233.                         }
  234.                     }
  235.                 
  236.                     // trim last eol
  237.                     $this->_testCase->expectedValue =
  238.                         substr($this->_testCase->expectedValue0-1);
  239.                     // reset state
  240.                     $this->_state = null;
  241.                     // append the test case
  242.                     $suite->addTestCase($this->_testCase);
  243.                 }
  244.             }
  245.             if ($suite{
  246.                 $ret[$suite;
  247.             }
  248.         }
  249.         return $ret;
  250.     }
  251.  
  252.     // }}}
  253.     // _parseFile() {{{
  254.  
  255.     /**
  256.      * Parse the file $file and return an array of Testing_DocTest_TestCase
  257.      * instances.
  258.      *
  259.      * @param string $file path to the file to parse.
  260.      *
  261.      * @access private
  262.      * @return array 
  263.      */
  264.     private function _parseFile($file)
  265.     {
  266.         $return = array();
  267.         $tokens $this->_tokenize($file);
  268.         if (false === $tokens{
  269.             // return an empty array
  270.             return $return;
  271.         }
  272.         $curlyLevel  = -1;
  273.         $curlyOpen   = -1;
  274.         $className   = null;
  275.         $insideQuote = false;
  276.         $insideClass = false;
  277.         while (false !== ($item each($tokens))) {
  278.             // memoize curly level in order to detect if we are inside a class
  279.             if (is_string($item['value'])) {
  280.                 if (!$insideQuote{
  281.                     if ($item['value'== '{'{
  282.                         $curlyLevel++;
  283.                     else if ($item['value'== '}' && --$curlyLevel == $curlyOpen{
  284.                         // curly is the close curly of current class
  285.                         $insideClass = false;
  286.                     }
  287.                 }
  288.                 if ($item['value'== '"'{
  289.                     $insideQuote !$insideQuote;
  290.                 }
  291.                 continue;
  292.             }
  293.             list($id$token$line$item['value'];
  294.             // skip all tokens but doc comments
  295.             if ($id !== T_DOC_COMMENT{
  296.                 continue;
  297.             }
  298.             // find next token
  299.             $ids  = array(T_CLASST_FUNCTIONT_DOC_COMMENT);
  300.             $next $this->_findNextToken($ids$tokens);
  301.             if (false === $next && !empty($return)) {
  302.                 break;
  303.             }
  304.             // build Testing_DocTest_TestCase instance
  305.             $ret               = array();
  306.             $ret['docComment'$token;
  307.             $ret['file']       $file;
  308.             if (false === $next || T_DOC_COMMENT === $next[0]{
  309.                 $ret['name']  'test';
  310.                 $ret['level''file level';
  311.             else {
  312.                 $nToken $this->_findNextToken(T_STRING$tokens);
  313.                 if (false === $nToken{
  314.                     continue;
  315.                 }
  316.                 if ($next[0=== T_CLASS{
  317.                     $insideClass  = true;
  318.                     $curlyOpen    $curlyLevel;
  319.                     $ret['name']  $nToken[1];
  320.                     $className    $nToken[1];
  321.                     $ret['level''class';
  322.                 else if ($insideClass{
  323.                     $ret['name']  $className '::' $nToken[1];
  324.                     $ret['level''method';
  325.                 else {
  326.                     $ret['name']  $nToken[1];
  327.                     $ret['level''function';
  328.                 }
  329.             }
  330.             $return[$ret;
  331.         }
  332.         return $return;
  333.     }
  334.  
  335.     // }}}
  336.     // _tokenize() {{{
  337.  
  338.     /**
  339.      * Tokenize the file $file into an array of tokens using the builtin php
  340.      * tokenizer extension. Before tokenizing the method check that the file
  341.      * contains at least a doctest.
  342.      *
  343.      * @param string $file the file to parse.
  344.      *
  345.      * @access private
  346.      * @return array array of tokens
  347.      */
  348.     private function _tokenize($file)
  349.     
  350.         $data file_get_contents($file);
  351.         // speed improvement, don't bother tokenizing file if it does not 
  352.         // contain any doctest
  353.         if (false === strstr($dataself::KW_DOCTEST_EXPECTS)) {
  354.             return array();
  355.         }
  356.         return token_get_all($data);
  357.     }
  358.  
  359.     // }}}
  360.     // _findNextToken() {{{
  361.  
  362.     /**
  363.      * Find the next token matching the id $id and return it or return false if
  364.      * no matching token is found.
  365.      *
  366.      * @param mixed $id      id or array of ids the token must match
  367.      * @param array &$tokens tokens array passed by reference
  368.      *
  369.      * @access private
  370.      * @return array array of tokens
  371.      */
  372.     private function _findNextToken($id&$tokens)
  373.     
  374.         $next current($tokens);
  375.         while ($next !== false{
  376.             if (!is_string($next)) {
  377.                 if (is_int($id&& $next[0=== $id{
  378.                     return $next;
  379.                 }
  380.                 if (is_array($id&& in_array($next[0]$id)) {
  381.                     return $next;
  382.                 }
  383.             }
  384.             // move to next token
  385.             $next next($tokens);
  386.         }
  387.         return false;
  388.     }
  389.  
  390.     // }}}
  391.     // _extractCodeBlocs() {{{
  392.  
  393.     /**
  394.      * Extract all <code></code> blocs in the given raw docstring.
  395.      *
  396.      * @param string $docstring raw docstring
  397.      *
  398.      * @access private
  399.      * @return array an array of code blocs strings.
  400.      */
  401.     private function _extractCodeBlocs($docstring)
  402.     {
  403.         $ret = array();
  404.         // extract <code></code> blocks, we use preg_match_all because there 
  405.         // could be more than one code block by docstring
  406.         $rx '/<code>[\s\*]*(<[\?\%](php)?)?\s*' 
  407.             . '(.*?)\s*([\?\%]>)?[\s\*]*<\/code>/si';
  408.         preg_match_all($rx$docstring$tokens);
  409.         if (isset($tokens[3]&& is_array($tokens[3])) {
  410.             foreach ($tokens[3as $i => $token{
  411.                 if ($this->_hasStandaloneDoctest($token)) {
  412.                     $testfile_contents $this->_handleStandaloneDoctest($token);
  413.                     if ($testfile_contents !== false{
  414.                         // replace the current doctest code with the contents
  415.                         // of the external included file
  416.                         $token $testfile_contents;
  417.                     }
  418.                 }
  419.                 if (!$this->_hasDocTest($token)) {
  420.                     // not a doctest
  421.                     continue;
  422.                 }
  423.                 $ret[$token;
  424.             }
  425.         }
  426.         return $ret;
  427.     }
  428.  
  429.     // }}}
  430.     // _hasStandaloneDoctest() {{{
  431.  
  432.     /**
  433.      * Return true if the string data provided contains an external doctest file.
  434.      *
  435.      * @param string $data The docstring data
  436.      *
  437.      * @return boolean 
  438.      */
  439.     private function _hasStandaloneDoctest($data)
  440.     {
  441.         $p preg_quote(self::SYNTAX_PREFIX'/');
  442.         $k preg_quote(self::KW_DOCTEST_FILE'/');
  443.         return preg_match("/$p\s?$k/m"$data);
  444.     }
  445.  
  446.     // }}}
  447.     // _hasDocTest() {{{
  448.  
  449.     /**
  450.      * Return true if the string data provided contains a doctest.
  451.      *
  452.      * @param string $data string data
  453.      *
  454.      * @access private
  455.      * @return boolean 
  456.      */
  457.     private function _hasDocTest($data)
  458.     {
  459.         $p preg_quote(self::SYNTAX_PREFIX'/');
  460.         $k preg_quote(self::KW_DOCTEST_EXPECTS'/');
  461.         return preg_match("/$p\s?$k/m"$data);
  462.     }
  463.  
  464.     // }}}
  465.     // _handleStandaloneDoctest() {{{
  466.  
  467.     /**
  468.      * Return the contents of the external doctest file.
  469.      *
  470.      * @param string $docbloc The docstring data
  471.      *
  472.      * @return mixed boolean or string
  473.      */
  474.     private function _handleStandaloneDoctest($docbloc)
  475.     {
  476.         $p     preg_quote(self::SYNTAX_PREFIX'/');
  477.         $k     preg_quote(self::KW_DOCTEST_FILE'/');
  478.         $lines preg_split('/(\n|\r\n)/'$docbloc);
  479.  
  480.         foreach ($lines as $i => $l{
  481.             $l preg_replace('/^\s*\*\s?/'''$l);
  482.             $p preg_quote(self::SYNTAX_PREFIX'/');
  483.             if (preg_match("/^\s*$p\s?($k):\s*(.*)$/"$l$matches)) {
  484.                 $f trim($matches[2]);
  485.                 if (false === ($contents @file_get_contents(realpath($f)))) {
  486.                     throw new Testing_DocTest_Exception(
  487.                         "Unable to read standalone doctest file \"$f\""
  488.                     );
  489.                 }
  490.                 // remove the php tags
  491.                 $rx '/(<[\?\%](php)?)?(.*?)([\?\%]>)?/si';
  492.                 return preg_replace($rx'\3'$contents);
  493.             }
  494.         }
  495.         return false;
  496.             
  497.     }
  498.  
  499.     // }}}
  500.     // _handleDoctestLine() {{{
  501.  
  502.     /**
  503.      * Parse the doctest line provided.
  504.      *
  505.      * @param string $line the line of code to parse
  506.      *
  507.      * @access private
  508.      * @return void 
  509.      * @throws Testing_DocTest_Exception
  510.      */
  511.     private function _handleDoctestLine($line)
  512.     {
  513.         $states = array(nullself::STATE_FLAGSself::STATE_DOCTEST,
  514.             self::STATE_SKIP_IFself::STATE_INI_SET);
  515.         if (!in_array($this->_state$states)) {
  516.             throw new Testing_DocTest_Exception("Unexpected doctest line: $line");
  517.         }
  518.         $this->_testCase->altname .= $line;
  519.         $this->_state              = self::STATE_DOCTEST;
  520.     }
  521.  
  522.     // }}}
  523.     // _handleFlagsLine() {{{
  524.  
  525.     /**
  526.      * Parse the flag line provided.
  527.      *
  528.      * @param string $line The flag line to parse
  529.      *
  530.      * @access private
  531.      * @return void 
  532.      * @throws Testing_DocTest_Exception
  533.      */
  534.     private function _handleFlagsLine($line)
  535.     {
  536.         $states = array(nullself::STATE_FLAGSself::STATE_DOCTEST,
  537.             self::STATE_SKIP_IFself::STATE_INI_SET);
  538.         if (!in_array($this->_state$states)) {
  539.             throw new Testing_DocTest_Exception("Unexpected flags line: $line");
  540.         }
  541.         $flags explode(','$line);
  542.         foreach ($flags as $flag{
  543.             $const 'Testing_DocTest::FLAG_' strtoupper(trim($flag));
  544.             if (defined($const)) {
  545.                 $this->_testCase->flags |= constant($const);
  546.             }
  547.         }
  548.         $this->_state = self::STATE_FLAGS;
  549.     }
  550.  
  551.     // }}}
  552.     // _handleExpectsLine() {{{
  553.  
  554.     /**
  555.      * Parse the expects line provided.
  556.      *
  557.      * @param string $line the expects line to parse
  558.      *
  559.      * @access private
  560.      * @return void 
  561.      * @throws Testing_DocTest_Exception
  562.      */
  563.     private function _handleExpectsLine($line)
  564.     {
  565.         $states = array(self::STATE_CODEself::STATE_EXPECTS);
  566.         if (!in_array($this->_state$states)) {
  567.             throw new Exception("unexpected expects line: $line");
  568.         }
  569.         $this->_testCase->expectedValue .= $line;
  570.         // handle line continuation
  571.         if (substr(trim($line)-1!== '\\'{
  572.             $this->_testCase->expectedValue .= "\n";
  573.         else {
  574.             $this->_testCase->expectedValue = 
  575.                 trim($this->_testCase->expectedValue'\\');
  576.         }
  577.         $this->_state = self::STATE_EXPECTS;
  578.     }
  579.  
  580.     // }}}
  581.     // _handleExpectsFileLine() {{{
  582.  
  583.     /**
  584.      * Parse the expects-file line provided.
  585.      *
  586.      * @param string $line the expects-file line to parse
  587.      *
  588.      * @access private
  589.      * @return void 
  590.      * @throws Testing_DocTest_Exception
  591.      */
  592.     private function _handleExpectsFileLine($line)
  593.     {
  594.         $states = array(self::STATE_CODEself::STATE_EXPECTS_FILE);
  595.         if (!in_array($this->_state$states)) {
  596.             throw new Exception("unexpected expects-file line: $line");
  597.         }
  598.         $f realpath(trim($line));
  599.         if (false === ($contents @file_get_contents($f))) {
  600.             throw new Testing_DocTest_Exception("Unable to read expects file $f");
  601.         }
  602.         $this->_testCase->expectedValue = $contents;
  603.         $this->_state                   = self::STATE_EXPECTS_FILE;
  604.     }
  605.  
  606.     // }}}
  607.     // _handleCodeLine() {{{
  608.  
  609.     /**
  610.      * Parse the code line provided.
  611.      *
  612.      * @param string $line the code line to parse
  613.      *
  614.      * @access private
  615.      * @return void 
  616.      * @throws Testing_DocTest_Exception
  617.      */
  618.     private function _handleCodeLine($line)
  619.     {
  620.         $states = array(self::STATE_EXPECTSself::STATE_EXPECTS_FILE);
  621.         if (in_array($this->_state$states)) {
  622.             throw new Testing_DocTest_Exception("Unexpected code line: $line");
  623.         }
  624.         $this->_testCase->code .= rtrim($line"\n";
  625.         $this->_state           = self::STATE_CODE;
  626.     }
  627.  
  628.     // }}}
  629.     // _handleSkipIfLine() {{{
  630.  
  631.     /**
  632.      * Parse the skip-if line provided.
  633.      *
  634.      * @param string $line the skip-if line to parse
  635.      *
  636.      * @access private
  637.      * @return void 
  638.      * @throws Testing_DocTest_Exception
  639.      */
  640.     private function _handleSkipIfLine($line)
  641.     {
  642.         $states = array(nullself::STATE_FLAGSself::STATE_DOCTEST,
  643.             self::STATE_SKIP_IFself::STATE_INI_SET);
  644.         if (!in_array($this->_state$states)) {
  645.             throw new Testing_DocTest_Exception("Unexpected skip-if line: $line");
  646.         }
  647.         $this->_testCase->skipIfCode .= rtrim($line"\n";
  648.         $this->_state                 = self::STATE_SKIP_IF;
  649.     }
  650.  
  651.     // }}}
  652.     // _handleIniSetLine() {{{
  653.  
  654.     /**
  655.      * Parse the ini-set line provided.
  656.      *
  657.      * @param string $line the ini-set line to parse
  658.      *
  659.      * @access private
  660.      * @return void 
  661.      * @throws Testing_DocTest_Exception
  662.      */
  663.     private function _handleIniSetLine($line)
  664.     {
  665.         $states = array(nullself::STATE_FLAGSself::STATE_DOCTEST,
  666.             self::STATE_SKIP_IFself::STATE_INI_SET);
  667.         if (!in_array($this->_state$states)) {
  668.             throw new Testing_DocTest_Exception("Unexpected ini-set line: $line");
  669.         }
  670.         $a explode('='trim($line));
  671.         if (count($a!= 2{
  672.             throw new Testing_DocTest_Exception("Malformed ini-set line: $line");
  673.         }
  674.         $this->_testCase->iniSettings[$a[0]] $a[1];
  675.         $this->_state                        = self::STATE_INI_SET;
  676.     }
  677.  
  678.     // }}}
  679.     // _handleCleanLine() {{{
  680.  
  681.     /**
  682.      * Parse the clean line provided.
  683.      *
  684.      * @param string $line the clean line to parse
  685.      *
  686.      * @access private
  687.      * @return void 
  688.      * @throws Testing_DocTest_Exception
  689.      */
  690.     private function _handleCleanLine($line)
  691.     {
  692.         $states = array(self::STATE_EXPECTSself::STATE_EXPECTS_FILE,
  693.             self::STATE_CLEAN);
  694.         if (!in_array($this->_state$states)) {
  695.             throw new Testing_DocTest_Exception("Unexpected clean line: $line");
  696.         }
  697.         $this->_testCase->cleanCode .= rtrim($line"\n";
  698.         $this->_state                = self::STATE_CLEAN;
  699.     }
  700.  
  701.     // }}}
  702.     // _handleLineContinuation() {{{
  703.  
  704.     /**
  705.      * Parse a line continuation.
  706.      *
  707.      * @param string $line the line to parse
  708.      *
  709.      * @access private
  710.      * @return void 
  711.      */
  712.     private function _handleLineContinuation($line)
  713.     {
  714.         switch ($this->_state{
  715.         case self::STATE_EXPECTS:
  716.             $this->_handleExpectsLine($line);
  717.             break;
  718.         case self::STATE_FLAGS:
  719.             $this->_handleFlagsLine($line);
  720.             break;
  721.         case self::STATE_DOCTEST:
  722.             $this->_handleDoctestLine($line);
  723.             break;
  724.         case self::STATE_SKIP_IF:
  725.             $this->_handleSkipIfLine($line);
  726.             break;
  727.         case self::STATE_INI_SET:
  728.             $this->_handleIniSetLine($line);
  729.             break;
  730.         case self::STATE_CLEAN:
  731.             $this->_handleCleanLine($line);
  732.         }
  733.     }
  734.  
  735.     // }}}
  736. }

Documentation generated on Mon, 11 Mar 2019 15:32:01 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.