home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / PHP / Beautifier.php next >
Encoding:
PHP Script  |  2008-07-02  |  43.8 KB  |  1,429 lines

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4.  * Contents Php_Beautifier class and make some tests
  5.  *
  6.  * PHP version 5
  7.  *
  8.  * LICENSE: This source file is subject to version 3.0 of the PHP license
  9.  * that is available through the world-wide-web at the following URI:
  10.  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
  11.  * the PHP License and are unable to obtain it through the web, please
  12.  * send a note to license@php.net so we can mail you a copy immediately.
  13.  * @category   PHP
  14.  * @package PHP_Beautifier
  15.  * @author Claudio Bustos <cdx@users.sourceforge.com>
  16.  * @copyright  2004-2006 Claudio Bustos
  17.  * @link     http://pear.php.net/package/PHP_Beautifier
  18.  * @link     http://beautifyphp.sourceforge.net
  19.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  20.  * @version    CVS: $Id:$
  21.  */
  22. error_reporting(E_ALL);
  23. // Before all, test the tokenizer extension
  24. if (!extension_loaded('tokenizer')) {
  25.     throw new Exception("Compile php with tokenizer extension. Use --enable-tokenizer or don't use --disable-all on configure.");
  26. }
  27. include_once 'PEAR.php';
  28. include_once 'PEAR/Exception.php';
  29. /**
  30.  * Require PHP_Beautifier_Filter
  31.  */
  32. include_once 'Beautifier/Filter.php';
  33. /**
  34.  * Require PHP_Beautifier_Filter_Default
  35.  */
  36. include_once 'Beautifier/Filter/Default.filter.php';
  37. /**
  38.  * Require PHP_Beautifier_Common
  39.  */
  40. include_once 'Beautifier/Common.php';
  41. /**
  42.  * Require Log
  43.  */
  44. include_once 'Log.php';
  45. /**
  46.  * Require Exceptions
  47.  */
  48. include_once 'Beautifier/Exception.php';
  49. /**
  50.  * Require StreamWrapper
  51.  */
  52. include_once 'Beautifier/StreamWrapper.php';
  53. /**
  54.  * PHP_Beautifier
  55.  *
  56.  * Class to beautify php code
  57.  * How to use:
  58.  * # Create a instance of the object
  59.  * # Define the input and output files
  60.  * # Optional: Set one or more Filter. They are processed in LIFO order (last in, first out)
  61.  * # Process the file
  62.  * # Get it, save it or show it.
  63.  *
  64.  * <code>
  65.  * $oToken = new PHP_Beautifier(); // create a instance
  66.  * $oToken->addFilter('ArraySimple');
  67.  * $oToken->addFilter('ListClassFunction'); // add one or more filters
  68.  * $oToken->setInputFile(__FILE__); // nice... process the same file
  69.  * $oToken->process(); // required
  70.  * $oToken->show();
  71.  * </code>
  72.  * @todo create a web interface.
  73.  * @category   PHP
  74.  * @package PHP_Beautifier
  75.  * @author Claudio Bustos <cdx@users.sourceforge.com>
  76.  * @copyright  2004-2006 Claudio Bustos
  77.  * @link     http://pear.php.net/package/PHP_Beautifier
  78.  * @link     http://beautifyphp.sourceforge.net
  79.  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
  80.  * @version    Release: 0.1.14
  81.  */
  82. class PHP_Beautifier implements PHP_Beautifier_Interface
  83. {
  84.     // public
  85.     
  86.     /**
  87.      * Tokens created by the tokenizer
  88.      * @var array
  89.      */
  90.     public $aTokens = array();
  91.     /**
  92.      * Tokens codes assigned to method on Filter
  93.      * @var array
  94.      */
  95.     public $aTokenFunctions = array();
  96.     /**
  97.      * Token Names
  98.      * @var array
  99.      */
  100.     public $aTokenNames = Array();
  101.     /**
  102.      * Stores the output
  103.      * @var array
  104.      */
  105.     public $aOut = array();
  106.     /**
  107.      * Contains the assigment of modes
  108.      * @var array
  109.      * @see setMode()
  110.      * @see unsetMode()
  111.      * @see getMode()
  112.      */
  113.     public $aModes = array();
  114.     /**
  115.      * List of availables modes
  116.      * @var array
  117.      */
  118.     public $aModesAvailable = array(
  119.         'ternary_operator',
  120.         'double_quote'
  121.     );
  122.     /**
  123.      * Settings for the class
  124.      * @var array
  125.      */
  126.     public $aSettings = array();
  127.     /**
  128.      * Index of current token
  129.      * @var int
  130.      */
  131.     public $iCount = 0;
  132.     /**
  133.      * Chars for indentation
  134.      * @var int
  135.      */
  136.     public $iIndentNumber = 4;
  137.     /**
  138.      * Level of array nesting
  139.      * @var int
  140.      */
  141.     public $iArray = 0;
  142.     /**
  143.      * Level of ternary operator nesting
  144.      * @var int
  145.      */
  146.     public $iTernary = 0;    
  147.     /**
  148.      * Level of parenthesis nesting
  149.      * @var int
  150.      */
  151.     public $iParenthesis = 0;
  152.     /**
  153.      * Level of verbosity (debug)
  154.      * @var int
  155.      */
  156.     public $iVerbose = false;
  157.     /**
  158.      * Name of input file
  159.      * @var string
  160.      */
  161.     public $sInputFile = '';
  162.     /**
  163.      * Name of output file
  164.      * @var string
  165.      */
  166.     public $sOutputFile = '';
  167.     /**
  168.      * Type of newline
  169.      * @var string
  170.      */
  171.     public $sNewLine = PHP_EOL;
  172.     /**
  173.      * Type of whitespace to use for indent
  174.      * @var string
  175.      */
  176.     public $sIndentChar = ' ';
  177.     /**
  178.      * Save the last whitespace used. Use only for Filter! (i miss friends of C++ :( )
  179.      * @var string
  180.      */
  181.     public $currentWhitespace = '';
  182.     /**
  183.      * Association $aTokens=>$aOut
  184.      * @var string
  185.      */
  186.     public $aAssocs = array();
  187.     // private
  188.     
  189.     /**
  190.      * type of file
  191.      */
  192.     private $sFileType = 'php';
  193.     /**
  194.      * Chars of indent
  195.      * @var int
  196.      */
  197.     private $iIndent = 0;
  198.     /**
  199.      * @var int
  200.      */
  201.     private $aIndentStack = array();
  202.     /** Text to beautify */
  203.     private $sText = '';
  204.     /** Constant for last Control */
  205.     private $iControlLast;
  206.     /** References to PHP_Beautifier_Filter's */
  207.     private $aFilters = array();
  208.     /**
  209.      * Stack with control construct
  210.      */
  211.     private $aControlSeq = array();
  212.     /**
  213.      * List of construct that start control structures
  214.      */
  215.     private $aControlStructures = array();
  216.     /**
  217.      * List of Control for parenthesis
  218.      */
  219.     private $aControlParenthesis = array();
  220.     /** 
  221.      * List of construct that end control structures
  222.      */
  223.     private $aControlStructuresEnd = array();
  224.     /** Dirs for Filters */
  225.     private $aFilterDirs = array();
  226.     /** Flag for beautify/no beautify mode */
  227.     private $bBeautify = true;
  228.     /** Log */
  229.     private $oLog;
  230.     /** Before new line holder */
  231.     private $sBeforeNewLine = null;
  232.     /** Activate or deactivate 'no delete previous space' */
  233.     private $bNdps = false;
  234.     // Methods
  235.     
  236.     /**
  237.      * Constructor.
  238.      * Assing values to {@link $aControlStructures},{@link $aControlStructuresEnd},
  239.      * {@link $aTokenFunctions}
  240.      */
  241.     public function __construct() 
  242.     {
  243.         $this->aControlStructures = array(
  244.             T_CLASS,
  245.             T_FUNCTION,
  246.             T_IF,
  247.             T_ELSE,
  248.             T_ELSEIF,
  249.             T_WHILE,
  250.             T_DO,
  251.             T_FOR,
  252.             T_FOREACH,
  253.             T_SWITCH,
  254.             T_DECLARE,
  255.             T_TRY,
  256.             T_CATCH
  257.         );
  258.         $this->aControlStructuresEnd = array(
  259.             T_ENDWHILE,
  260.             T_ENDFOREACH,
  261.             T_ENDFOR,
  262.             T_ENDDECLARE,
  263.             T_ENDSWITCH,
  264.             T_ENDIF
  265.         );
  266.         $aPreTokens = preg_grep('/^T_/', array_keys(get_defined_constants()));
  267.         foreach($aPreTokens as $sToken) {
  268.             $this->aTokenNames[constant($sToken) ] = $sToken;
  269.             $this->aTokenFunctions[constant($sToken) ] = $sToken;
  270.         }
  271.         $aTokensToChange = array(
  272.             /* QUOTES */
  273.             '"' => "T_DOUBLE_QUOTE",
  274.             "'" => "T_SINGLE_QUOTE",
  275.             /* PUNCTUATION */
  276.             '(' => 'T_PARENTHESIS_OPEN',
  277.             ')' => 'T_PARENTHESIS_CLOSE',
  278.             ';' => 'T_SEMI_COLON',
  279.             '{' => 'T_OPEN_BRACE',
  280.             '}' => 'T_CLOSE_BRACE',
  281.             ',' => 'T_COMMA',
  282.             '?' => 'T_QUESTION',
  283.             ':' => 'T_COLON',
  284.             '=' => 'T_ASSIGMENT',
  285.             '<' => 'T_EQUAL',
  286.             '>' => 'T_EQUAL',
  287.             '.' => 'T_DOT',
  288.             '[' => 'T_OPEN_SQUARE_BRACE',
  289.             ']' => 'T_CLOSE_SQUARE_BRACE',
  290.             /* OPERATOR*/
  291.             '+' => 'T_OPERATOR',
  292.             '-' => 'T_OPERATOR',
  293.             '*' => 'T_OPERATOR',
  294.             '/' => 'T_OPERATOR',
  295.             '%' => 'T_OPERATOR',
  296.             '&' => 'T_OPERATOR',
  297.             '|' => 'T_OPERATOR',
  298.             '^' => 'T_OPERATOR',
  299.             '~' => 'T_OPERATOR',
  300.             T_SL => 'T_OPERATOR',
  301.             T_SR => 'T_OPERATOR',
  302.             T_OBJECT_OPERATOR => 'T_OBJECT_OPERATOR',
  303.             /* INCLUDE */
  304.             T_INCLUDE => 'T_INCLUDE',
  305.             T_INCLUDE_ONCE => 'T_INCLUDE',
  306.             T_REQUIRE => 'T_INCLUDE',
  307.             T_REQUIRE_ONCE => 'T_INCLUDE',
  308.             /* LANGUAGE CONSTRUCT */
  309.             T_FUNCTION => 'T_LANGUAGE_CONSTRUCT',
  310.             T_PRINT => 'T_LANGUAGE_CONSTRUCT',
  311.             T_RETURN => 'T_LANGUAGE_CONSTRUCT',
  312.             T_ECHO => 'T_LANGUAGE_CONSTRUCT',
  313.             T_NEW => 'T_LANGUAGE_CONSTRUCT',
  314.             T_CLASS => 'T_LANGUAGE_CONSTRUCT',
  315.             T_VAR => 'T_LANGUAGE_CONSTRUCT',
  316.             T_GLOBAL => 'T_LANGUAGE_CONSTRUCT',
  317.             T_THROW => 'T_LANGUAGE_CONSTRUCT',
  318.             /* CONTROL */
  319.             T_IF => 'T_CONTROL',
  320.             T_DO => 'T_CONTROL',
  321.             T_WHILE => 'T_CONTROL',
  322.             T_SWITCH => 'T_CONTROL',
  323.             /* ELSE */
  324.             T_ELSEIF => 'T_ELSE',
  325.             T_ELSE => 'T_ELSE',
  326.             /* ACCESS PHP 5 */
  327.             T_INTERFACE => 'T_ACCESS',
  328.             T_FINAL => 'T_ACCESS',
  329.             T_ABSTRACT => 'T_ACCESS',
  330.             T_PRIVATE => 'T_ACCESS',
  331.             T_PUBLIC => 'T_ACCESS',
  332.             T_PROTECTED => 'T_ACCESS',
  333.             T_CONST => 'T_ACCESS',
  334.             T_STATIC => 'T_ACCESS',
  335.             /* COMPARATORS */
  336.             T_PLUS_EQUAL => 'T_ASSIGMENT_PRE',
  337.             T_MINUS_EQUAL => 'T_ASSIGMENT_PRE',
  338.             T_MUL_EQUAL => 'T_ASSIGMENT_PRE',
  339.             T_DIV_EQUAL => 'T_ASSIGMENT_PRE',
  340.             T_CONCAT_EQUAL => 'T_ASSIGMENT_PRE',
  341.             T_MOD_EQUAL => 'T_ASSIGMENT_PRE',
  342.             T_AND_EQUAL => 'T_ASSIGMENT_PRE',
  343.             T_OR_EQUAL => 'T_ASSIGMENT_PRE',
  344.             T_XOR_EQUAL => 'T_ASSIGMENT_PRE',
  345.             T_DOUBLE_ARROW => 'T_ASSIGMENT',
  346.             T_SL_EQUAL => 'T_EQUAL',
  347.             T_SR_EQUAL => 'T_EQUAL',
  348.             T_IS_EQUAL => 'T_EQUAL',
  349.             T_IS_NOT_EQUAL => 'T_EQUAL',
  350.             T_IS_IDENTICAL => 'T_EQUAL',
  351.             T_IS_NOT_IDENTICAL => 'T_EQUAL',
  352.             T_IS_SMALLER_OR_EQUAL => 'T_EQUAL',
  353.             T_IS_GREATER_OR_EQUAL => 'T_EQUAL',
  354.             /* LOGICAL*/
  355.             T_LOGICAL_OR => 'T_LOGICAL',
  356.             T_LOGICAL_XOR => 'T_LOGICAL',
  357.             T_LOGICAL_AND => 'T_LOGICAL',
  358.             T_BOOLEAN_OR => 'T_LOGICAL',
  359.             T_BOOLEAN_AND => 'T_LOGICAL',
  360.             /* SUFIX END */
  361.             T_ENDWHILE => 'T_END_SUFFIX',
  362.             T_ENDFOREACH => 'T_END_SUFFIX',
  363.             T_ENDFOR => 'T_END_SUFFIX',
  364.             T_ENDDECLARE => 'T_END_SUFFIX',
  365.             T_ENDSWITCH => 'T_END_SUFFIX',
  366.             T_ENDIF => 'T_END_SUFFIX',
  367.         );
  368.         foreach($aTokensToChange as $iToken => $sFunction) {
  369.             $this->aTokenFunctions[$iToken] = $sFunction;
  370.         }
  371.         $this->addFilterDirectory(dirname(__FILE__) . '/Beautifier/Filter');
  372.         $this->addFilter('Default');
  373.         $this->oLog = PHP_Beautifier_Common::getLog();
  374.     }
  375.     public function getTokenName($iToken) 
  376.     {
  377.         if(!$iToken) {
  378.             throw new Exception("Token $iToken doesn't exists");
  379.         }
  380.         return $this->aTokenNames[$iToken];
  381.     }
  382.     /**
  383.      * Start the log for debug
  384.      * @param    string  filename
  385.      * @param    int     debug level. See {@link Log}
  386.      */
  387.     public function startLog($sFile = 'php_beautifier.log', $iLevel = PEAR_LOG_DEBUG) 
  388.     {
  389.         @unlink($sFile);
  390.         $oLogFile = Log::factory('file', $sFile, 'php_beautifier', array() , PEAR_LOG_DEBUG);
  391.         $this->oLog->addChild($oLogFile);
  392.     }
  393.     /**
  394.      * Add a filter directory
  395.      * @param string path to directory
  396.      * @throws Exception
  397.      */
  398.     public function addFilterDirectory($sDir) 
  399.     {
  400.         $sDir = PHP_Beautifier_Common::normalizeDir($sDir);
  401.         if (file_exists($sDir)) {
  402.             array_push($this->aFilterDirs, $sDir);
  403.         } else {
  404.             throw new Exception_PHP_Beautifier_Filter("Path '$sDir' doesn't exists");
  405.         }
  406.     }
  407.     /**
  408.      * Return an array with all the Filter Dirs
  409.      * @return array     List of Filter Directories
  410.      */
  411.     public function getFilterDirectories() 
  412.     {
  413.         return $this->aFilterDirs;
  414.     }
  415.     private function addFilterObject(PHP_Beautifier_Filter $oFilter) 
  416.     {
  417.         array_unshift($this->aFilters, $oFilter);
  418.         return true;
  419.     }
  420.     /**
  421.      * Add a Filter to the Beautifier
  422.      * The first argument is the name of the file of the Filter.
  423.      * @tutorial PHP_Beautifier/Filter/Filter2.pkg#use
  424.      * @param  string name of the Filter
  425.      * @param  array settings for the Filter
  426.      * @return bool true if Filter is loaded, false if the same filter was loaded previously
  427.      * @throws  Exception
  428.      */
  429.     public function addFilter($mFilter, $aSettings = array()) 
  430.     {
  431.         if ($mFilter instanceOf PHP_Beautifier_Filter) {
  432.             return $this->addFilterObject($mFilter);
  433.         }
  434.         $sFilterClass = 'PHP_Beautifier_Filter_' . $mFilter;
  435.         if (!class_exists($sFilterClass)) {
  436.             $this->addFilterFile($mFilter);
  437.         }
  438.         $oTemp = new $sFilterClass($this, $aSettings);
  439.         // verify if same Filter is loaded
  440.         if (in_array($oTemp, $this->aFilters, TRUE)) {
  441.             return false;
  442.         } elseif ($oTemp instanceof PHP_Beautifier_Filter) {
  443.             $this->addFilterObject($oTemp);
  444.         } else {
  445.             throw new Exception_PHP_Beautifier_Filter("'$sFilterClass' isn't a subclass of 'Filter'");
  446.         }
  447.     }
  448.     /**
  449.      * Removes a Filter
  450.      * @param    string  name of the filter
  451.      * @return   bool    true if Filter is removed, false otherwise
  452.      */
  453.     public function removeFilter($sFilter) 
  454.     {
  455.         $sFilterName = strtolower('PHP_Beautifier_Filter_' . $sFilter);
  456.         foreach($this->aFilters as $sId => $oFilter) {
  457.             if (strtolower(get_class($oFilter)) == $sFilterName) {
  458.                 unset($this->aFilters[$sId]);
  459.                 return true;
  460.             }
  461.         }
  462.         return false;
  463.     }
  464.     /**
  465.      * Return the Filter Description
  466.      * @see PHP_Beautifier_Filter::__toString();
  467.      * @param    string  name of the filter
  468.      * @return   mixed   string or false
  469.      */
  470.     public function getFilterDescription($sFilter) 
  471.     {
  472.         $aFilters = $this->getFilterListTotal();
  473.         if (in_array($sFilter, $aFilters)) {
  474.             $this->addFilterFile($sFilter);
  475.             $sFilterClass = 'PHP_Beautifier_Filter_' . $sFilter;
  476.             $oTemp = new $sFilterClass($this, array());
  477.             return $oTemp;
  478.         } else {
  479.             return false;
  480.         }
  481.     }
  482.     /**
  483.      * Add a new filter to the processor.
  484.      * The system will process the filter in LIFO order
  485.      * @param    string  name of filter
  486.      * @see process()
  487.      * @return bool
  488.      * @throws  Exception
  489.      */
  490.     private function addFilterFile($sFilter) 
  491.     {
  492.         $sFilterClass = 'PHP_Beautifier_Filter_' . $sFilter;
  493.         if (class_exists($sFilterClass)) {
  494.             return true;
  495.         }
  496.         foreach($this->aFilterDirs as $sDir) {
  497.             $sFile = $sDir . $sFilter . '.filter.php';
  498.             if (file_exists($sFile)) {
  499.                 include_once $sFile;
  500.                 if (class_exists($sFilterClass)) {
  501.                     return true;
  502.                 } else {
  503.                     throw new Exception_PHP_Beautifier_Filter("File '$sFile' exists,but doesn't exists filter '$sFilterClass'");
  504.                 }
  505.             }
  506.         }
  507.         throw new Exception_PHP_Beautifier_Filter("Doesn't exists filter '$sFilter'");
  508.     }
  509.     /**
  510.      * Get the names of the loaded filters
  511.      * @return array list of Filters
  512.      */
  513.     public function getFilterList() 
  514.     {
  515.         foreach($this->aFilters as $oFilter) {
  516.             $aOut[] = $oFilter->getName();
  517.         }
  518.         return $aOut;
  519.     }
  520.     /**
  521.      * Get the list of all available Filters in all the include Dirs
  522.      * @return array list of Filters
  523.      */
  524.     public function getFilterListTotal() 
  525.     {
  526.         $aFilterFiles = array();
  527.         foreach($this->aFilterDirs as $sDir) {
  528.             $aFiles = PHP_Beautifier_Common::getFilesByPattern($sDir, ".*?\.filter\.php");
  529.             array_walk($aFiles, array(
  530.                 $this,
  531.                 'getFilterList_FilterName'
  532.             ));
  533.             $aFilterFiles = array_merge($aFilterFiles, $aFiles);
  534.         }
  535.         sort($aFilterFiles);
  536.         return $aFilterFiles;
  537.     }
  538.     /**
  539.      * Receive a path to a filter and replace it with the name of filter
  540.      */
  541.     private function getFilterList_FilterName(&$sFile) 
  542.     {
  543.         preg_match("/\/([^\/]*?)\.filter\.php/", $sFile, $aMatch);
  544.         $sFile = $aMatch[1];
  545.     }
  546.     public function getIndentChar() 
  547.     {
  548.         return $this->sIndentChar;
  549.     }
  550.     public function getIndentNumber() 
  551.     {
  552.         return $this->iIndentNumber;
  553.     }
  554.     public function getNewLine() 
  555.     {
  556.         return $this->sNewLine;
  557.     }
  558.     /**
  559.      * Character used for indentation
  560.      * @param string usually ' ' or "\t"
  561.      */
  562.     public function setIndentChar($sChar) 
  563.     {
  564.         $this->sIndentChar = $sChar;
  565.     }
  566.     /**
  567.      * Number of characters for indentation
  568.      * @param int ussualy 4 for space or 1 for tabs
  569.      */
  570.     public function setIndentNumber($iIndentNumber) 
  571.     {
  572.         $this->iIndentNumber = $iIndentNumber;
  573.     }
  574.     /**
  575.      * Character used as a new line
  576.      * @param string ussualy "\n", "\r\n" or "\r"
  577.      */
  578.     public function setNewLine($sNewLine) 
  579.     {
  580.         $this->sNewLine = $sNewLine;
  581.     }
  582.     /**
  583.      * Set the file for beautify
  584.      * @param string path to file
  585.      * @throws Exception
  586.      */
  587.     public function setInputFile($sFile) 
  588.     {
  589.         $bCli = (php_sapi_name() == 'cli');
  590.         if (strpos($sFile, '://') === FALSE and !file_exists($sFile) and !($bCli and $sFile == STDIN)) {
  591.             throw new Exception("File '$sFile' doesn't exists");
  592.         }
  593.         $this->sText = '';
  594.         $this->sInputFile = $sFile;
  595.         $fp = ($bCli and $sFile == STDIN) ? STDIN : fopen($sFile, 'r');
  596.         do {
  597.             $data = fread($fp, 8192);
  598.             if (strlen($data) == 0) {
  599.                 break;
  600.             }
  601.             $this->sText.= $data;
  602.         }
  603.         while (true);
  604.         if (!($bCli and $fp == STDIN)) {
  605.             fclose($fp);
  606.         }
  607.         return true;
  608.     }
  609.     /**
  610.      * Set the output file for beautify
  611.      * @param string path to file
  612.      */
  613.     public function setOutputFile($sFile) 
  614.     {
  615.         $this->sOutputFile = $sFile;
  616.     }
  617.     /**
  618.      * Save the beautified code to output file
  619.      * @param string path to file. If null, {@link $sOutputFile} if exists, throw exception otherwise
  620.      * @see setOutputFile();
  621.      * @throws Exception
  622.      */
  623.     public function save($sFile = null) 
  624.     {
  625.         $bCli = (php_sapi_name() == 'cli');
  626.         if (!$sFile) {
  627.             if (!$this->sOutputFile) {
  628.                 throw new Exception("Can't save without a output file");
  629.             } else {
  630.                 $sFile = $this->sOutputFile;
  631.             }
  632.         }
  633.         $sText = $this->get();
  634.         $fp = ($bCli and $sFile == STDOUT) ? STDOUT : @fopen($sFile, "w");
  635.         if (!$fp) {
  636.             throw new Exception("Can't save file $sFile");
  637.         }
  638.         fputs($fp, $sText, strlen($sText));
  639.         if (!($bCli and $sFile == STDOUT)) {
  640.             fclose($fp);
  641.         }
  642.         $this->oLog->log("Success: $sFile saved", PEAR_LOG_INFO);
  643.         return true;
  644.     }
  645.     /**
  646.      * Set a string for beautify
  647.      * @param string Must be preceded by open tag
  648.      */
  649.     public function setInputString($sText) 
  650.     {
  651.         $this->sText = $sText;
  652.     }
  653.     /**
  654.      * Reset all properties
  655.      */
  656.     private function resetProperties() 
  657.     {
  658.         $aProperties = array(
  659.             'aTokens' => array() ,
  660.             'aOut' => array() ,
  661.             'aModes' => array() ,
  662.             'iCount' => 0,
  663.             'iIndent' => 0
  664.             /*$this->iIndentNumber*/
  665.             ,
  666.             'aIndentStack' => array(
  667.                 /*$this->iIndentNumber*/
  668.             ) ,
  669.             'iArray' => 0,
  670.             'iParenthesis' => 0,
  671.             'currentWhitespace' => '',
  672.             'aAssocs' => array() ,
  673.             'iControlLast' => null,
  674.             'aControlSeq' => array() ,
  675.             'bBeautify' => true
  676.         );
  677.         foreach($aProperties as $sProperty => $sValue) {
  678.             $this->$sProperty = $sValue;
  679.         }
  680.     }
  681.     /**
  682.      * Process the string or file to beautify
  683.      * @return bool true on success
  684.      * @throws Exception
  685.      */
  686.     public function process() 
  687.     {
  688.         $this->oLog->log('Init process of ' . (($this->sInputFile) ? 'file ' . $this->sInputFile : 'string') , PEAR_LOG_DEBUG);
  689.         $this->resetProperties();
  690.         // if file type is php, use token_get_all
  691.         // else, use a class named PHP_Beautifier_Tokenizer_XXX
  692.         // instanced with the text and get the tokens with
  693.         // getTokens()
  694.         if ($this->sFileType == 'php') {
  695.             $this->aTokens = token_get_all($this->sText);
  696.         } else {
  697.             $sClass = 'PHP_Beautifier_Tokeniker_' . ucfirst($this->sFileType);
  698.             if (class_exists($sClass)) {
  699.                 $oTokenizer = new $sClass($this->sText);
  700.                 $this->aTokens = $oTokenizer->getTokens();
  701.             } else {
  702.                 throw new Exception("File type " . $this->sFileType . " not implemented");
  703.             }
  704.         }
  705.         $this->aOut = array();
  706.         $iTotal = count($this->aTokens);
  707.         $iPrevAssoc = false;
  708.         // Send a signal to the filter, announcing the init of the processing of a file
  709.         foreach($this->aFilters as $oFilter) {
  710.             $oFilter->preProcess();
  711.         }
  712.         for ($this->iCount = 0 ; $this->iCount < $iTotal ; $this->iCount++) {
  713.             $aCurrentToken = $this->aTokens[$this->iCount];
  714.             if (is_string($aCurrentToken)) {
  715.                 $aCurrentToken = array(
  716.                     0 => $aCurrentToken,
  717.                     1 => $aCurrentToken
  718.                 );
  719.             }
  720.             // ArrayNested->off();
  721.             $sTextLog = PHP_Beautifier_Common::wsToString($aCurrentToken[1]);
  722.             // ArrayNested->on();
  723.             $sTokenName = (is_numeric($aCurrentToken[0])) ? token_name($aCurrentToken[0]) : '';
  724.             $this->oLog->log("Token:" . $sTokenName . "[" . $sTextLog . "]", PEAR_LOG_DEBUG);
  725.             $this->controlToken($aCurrentToken);
  726.             $iFirstOut = count($this->aOut); //5
  727.             $bError = false;
  728.             if ($this->bBeautify) {
  729.                 foreach($this->aFilters as $oFilter) {
  730.                     $bError = true;
  731.                     if ($oFilter->handleToken($aCurrentToken) !== FALSE) {
  732.                         $this->oLog->log('Filter:' . $oFilter->getName() , PEAR_LOG_DEBUG);
  733.                         $bError = false;
  734.                         break;
  735.                     }
  736.                 }
  737.             } else {
  738.                 $this->add($aCurrentToken[1]);
  739.             }
  740.             $this->controlTokenPost($aCurrentToken);
  741.             $iLastOut = count($this->aOut);
  742.             // set the assoc
  743.             if (($iLastOut-$iFirstOut) > 0) {
  744.                 $this->aAssocs[$this->iCount] = array(
  745.                     'offset' => $iFirstOut
  746.                 );
  747.                 if ($iPrevAssoc !== FALSE) {
  748.                     $this->aAssocs[$iPrevAssoc]['length'] = $iFirstOut-$this->aAssocs[$iPrevAssoc]['offset'];
  749.                 }
  750.                 $iPrevAssoc = $this->iCount;
  751.             }
  752.             if ($bError) {
  753.                 throw new Exception("Can'process token: " . var_dump($aCurrentToken));
  754.             }
  755.         } // ~for
  756.         // generate the last assoc
  757.         if (count($this->aOut) == 0) {
  758.             if ($this->sFile) {
  759.                 throw new Exception("Nothing on output for " . $this->sFile . "!");
  760.             } else {
  761.                 throw new Exception("Nothing on output!");
  762.             }
  763.         }
  764.         $this->aAssocs[$iPrevAssoc]['length'] = (count($this->aOut) -1) -$this->aAssocs[$iPrevAssoc]['offset'];
  765.         // Post-processing
  766.         foreach($this->aFilters as $oFilter) {
  767.             $oFilter->postProcess();
  768.         }
  769.         $this->oLog->log('End process', PEAR_LOG_DEBUG);
  770.         return true;
  771.     }
  772.     /**
  773.      * Get the reference to {@link $aOut}, based on the number of the token
  774.      * @param int token number
  775.      * @return mixed false array or false if token doesn't exists
  776.      */
  777.     public function getTokenAssoc($iIndex) 
  778.     {
  779.         return (array_key_exists($iIndex, $this->aAssocs)) ? $this->aAssocs[$iIndex] : false;
  780.     }
  781.     /**
  782.      * Get the output for the specified token
  783.      * @param int token number
  784.      * @return mixed string or false if token doesn't exists
  785.      */
  786.     public function getTokenAssocText($iIndex) 
  787.     {
  788.         if (!($aAssoc = $this->getTokenAssoc($iIndex))) {
  789.             return false;
  790.         }
  791.         return (implode('', array_slice($this->aOut, $aAssoc['offset'], $aAssoc['length'])));
  792.     }
  793.     /**
  794.      * Replace the output for specified token
  795.      * @param int     token number
  796.      * @param string  replace text
  797.      * @return bool
  798.      */
  799.     public function replaceTokenAssoc($iIndex, $sText) 
  800.     {
  801.         if (!($aAssoc = $this->getTokenAssoc($iIndex))) {
  802.             return false;
  803.         }
  804.         $this->aOut[$aAssoc['offset']] = $sText;
  805.         for ($x = 0 ; $x < $aAssoc['length']-1 ; $x++) {
  806.             $this->aOut[$aAssoc['offset']+$x+1] = '';
  807.         }
  808.         return true;
  809.     }
  810.     /**
  811.      * Return the function for a token constant or string.
  812.      * @param mixed  token constant or string
  813.      * @return mixed name of function or false
  814.      */
  815.     public function getTokenFunction($sTokenType) 
  816.     {
  817.         return (array_key_exists($sTokenType, $this->aTokenFunctions)) ? strtolower($this->aTokenFunctions[$sTokenType]) : false;
  818.     }
  819.     /**
  820.      * Process a callback from the code to beautify
  821.      * @param    array   third parameter from preg_match
  822.      * @return   bool
  823.      * @uses controlToken()
  824.      */
  825.     private function processCallback($aMatch) 
  826.     {
  827.         if (stristr('php_beautifier', $aMatch[1]) and method_exists($this, $aMatch[3])) {
  828.             if (preg_match("/^(set|add)/i", $aMatch[3]) and !stristr('file', $aMatch[3])) {
  829.                 eval('$this->' . $aMatch[2] . ";");
  830.                 return true;
  831.             }
  832.         } else {
  833.             foreach($this->aFilters as $iIndex => $oFilter) {
  834.                 if (strtolower(get_class($oFilter)) == 'php_beautifier_filter_' . strtolower($aMatch[1]) and method_exists($oFilter, $aMatch[3])) {
  835.                     eval('$this->aFilters[' . $iIndex . ']->' . $aMatch[2] . ";");
  836.                     return true;
  837.                 }
  838.             }
  839.         }
  840.         return false;
  841.     }
  842.     /**
  843.      * Assign value for some variables with the information of the token
  844.      * @param array current token
  845.      */
  846.     private function controlToken($aCurrentToken) 
  847.     {
  848.         // is a control structure opener?
  849.         if (in_array($aCurrentToken[0], $this->aControlStructures)) {
  850.             $this->pushControlSeq($aCurrentToken);
  851.             $this->iControlLast = $aCurrentToken[0];
  852.         }
  853.         // is a control structure closer?
  854.         if (in_array($aCurrentToken[0], $this->aControlStructuresEnd)) {
  855.             $this->popControlSeq();
  856.         }
  857.         switch ($aCurrentToken[0]) {
  858.             case T_COMMENT:
  859.                 // callback!
  860.                 if (preg_match("/\/\/\s*(.*?)->((.*)\((.*)\))/", $aCurrentToken[1], $aMatch)) {
  861.                     try {
  862.                         $this->processCallback($aMatch);
  863.                     }
  864.                     catch(Exception $oExp) {
  865.                     }
  866.                 }
  867.                 break;
  868.  
  869.             case T_FUNCTION:
  870.                 $this->setMode('function');
  871.                 break;
  872.  
  873.             case T_CLASS:
  874.                 $this->setMode('class');
  875.                 break;
  876.  
  877.             case T_ARRAY:
  878.                 $this->iArray++;
  879.                 break;
  880.  
  881.             case T_WHITESPACE:
  882.                 $this->currentWhitespace = $aCurrentToken[1];
  883.                 break;
  884.  
  885.             case '{':
  886.                 if ($this->isPreviousTokenConstant(T_VARIABLE) or ($this->isPreviousTokenConstant(T_STRING) and $this->getPreviousTokenConstant(2) == T_OBJECT_OPERATOR) or $this->isPreviousTokenConstant(T_OBJECT_OPERATOR)) {
  887.                     $this->setMode('string_index');
  888.                 }
  889.                 break;
  890.  
  891.             case '(':
  892.                 $this->iParenthesis++;
  893.                 $this->pushControlParenthesis();
  894.                 break;
  895.  
  896.             case ')':
  897.                 $this->iParenthesis--;
  898.                 break;
  899.  
  900.             case '?':
  901.                 $this->setMode('ternary_operator');
  902.                 $this->iTernary++;
  903.                 break;
  904.  
  905.             case '"':
  906.                 ($this->getMode('double_quote')) ? $this->unsetMode('double_quote') : $this->setMode('double_quote');
  907.                 break;
  908.  
  909.             case T_START_HEREDOC:
  910.                 $this->setMode('double_quote');
  911.                 break;
  912.  
  913.             case T_END_HEREDOC:
  914.                 $this->unsetMode('double_quote');
  915.                 break;
  916.         }
  917.         if ($this->getTokenFunction($aCurrentToken[0]) == 't_include') {
  918.             $this->setMode('include');
  919.         }
  920.     }
  921.     /**
  922.      * Assign value for some variables with the information of the token, after processing
  923.      * @param array current token
  924.      */
  925.     private function controlTokenPost($aCurrentToken) 
  926.     {
  927.         switch ($aCurrentToken[0]) {
  928.             case ')':
  929.                 if ($this->iArray) {
  930.                     $this->iArray--;
  931.                 }
  932.                 $this->popControlParenthesis();
  933.                 break;
  934.  
  935.             case '}':
  936.                 if ($this->getMode('string_index')) {
  937.                     $this->unsetMode('string_index');
  938.                 } else {
  939.                     $this->oLog->log('end bracket:' . $this->getPreviousTokenContent() , PEAR_LOG_DEBUG);
  940.                     if ($this->getPreviousTokenContent() == ';' or $this->getPreviousTokenContent() == '}' or $this->getPreviousTokenContent() == '{') {
  941.                         $this->popControlSeq();
  942.                     }
  943.                 }
  944.                 break;
  945.  
  946.             case '{':
  947.                 $this->unsetMode('function');
  948.                 break;
  949.         }
  950.         if ($this->getTokenFunction($aCurrentToken[0]) == 't_colon') {
  951.             if ($this->iTernary) {
  952.                     $this->iTernary--;
  953.             }
  954.             if(!$this->iTernary) {
  955.                 $this->unsetMode('ternary_operator');
  956.             }
  957.         }
  958.     }
  959.     /**
  960.      * Push a control construct to the stack
  961.      * @param array current token
  962.      */
  963.     private function pushControlSeq($aToken) 
  964.     {
  965.         $this->oLog->log('Push Control:' . $aToken[0] . "->" . $aToken[1], PEAR_LOG_DEBUG);
  966.         array_push($this->aControlSeq, $aToken[0]);
  967.     }
  968.     /**
  969.      * Pop a control construct from the stack
  970.      * @return int token constant
  971.      */
  972.     private function popControlSeq() 
  973.     {
  974.         $aEl = array_pop($this->aControlSeq);
  975.         $this->oLog->log('Pop Control:' . $this->getTokenName($aEl) , PEAR_LOG_DEBUG);
  976.         return $aEl;
  977.     }
  978.     /**
  979.      * Push a new Control Instruction on the stack
  980.      */
  981.     private function pushControlParenthesis() 
  982.     {
  983.         $iPrevious = $this->getPreviousTokenConstant();
  984.         $this->oLog->log("Push Parenthesis: $iPrevious ->" . $this->getPreviousTokenContent() , PEAR_LOG_DEBUG);
  985.         array_push($this->aControlParenthesis, $iPrevious);
  986.     }
  987.     /**
  988.      * Pop the last Control instruction for parenthesis from the stack
  989.      * @return int   constant
  990.      */
  991.     private function popControlParenthesis() 
  992.     {
  993.         $iPop = array_pop($this->aControlParenthesis);
  994.         $this->oLog->log('Pop Parenthesis:' . $iPop, PEAR_LOG_DEBUG);
  995.         return $iPop;
  996.     }
  997.     /**
  998.      * Set the filetype
  999.      * @param string
  1000.      */
  1001.     public function setFileType($sType) 
  1002.     {
  1003.         $this->sFileType = $sType;
  1004.     }
  1005.     /**
  1006.      * Set the Beautifier on or off
  1007.      * @param bool
  1008.      */
  1009.     public function setBeautify($sFlag) 
  1010.     {
  1011.         $this->bBeautify = (bool)$sFlag;
  1012.     }
  1013.     /**
  1014.      * Show the beautified code
  1015.      */
  1016.     public function show() 
  1017.     {
  1018.         echo $this->get();
  1019.     }
  1020.     /**
  1021.      * Activate or deactivate this ominous hack
  1022.      * If you need to maintain some special whitespace
  1023.      * you can activate this hack and use (delete the space between * and /)
  1024.      * <code>/**ndps** /</code>
  1025.      * in {@link get()}, this text will be erased.
  1026.      * @see removeWhitespace()
  1027.      * @see PHP_Beautifier_Filter_NewLines
  1028.      */
  1029.     function setNoDeletePreviousSpaceHack($bFlag = true) 
  1030.     {
  1031.         $this->bNdps = $bFlag;
  1032.     }
  1033.     /**
  1034.      * Returns the beautified code
  1035.      * @see setNoDeletePreviousSpaceHack()
  1036.      * @return string
  1037.      */
  1038.     public function get() 
  1039.     {
  1040.         if (!$this->bNdps) {
  1041.             return implode('', $this->aOut);
  1042.         } else {
  1043.             return str_replace('/**ndps**/', '', implode('', $this->aOut));
  1044.         }
  1045.     }
  1046.     /**
  1047.      * Returns the value of a settings
  1048.      * @param string Name of the setting
  1049.      * @return mixed Value of the settings or false
  1050.      */
  1051.     public function getSetting($sKey) 
  1052.     {
  1053.         return (array_key_exists($sKey, $this->aSettings)) ? $this->aSettings[$sKey] : false;
  1054.     }
  1055.     /**
  1056.      * Get the token constant for the current control construct
  1057.      * @param int   current token -'x'
  1058.      *@ return mixed token constant or false
  1059.      */
  1060.     public function getControlSeq($iRet = 0) 
  1061.     {
  1062.         $iIndex = count($this->aControlSeq) -$iRet-1;
  1063.         return ($iIndex >= 0) ? $this->aControlSeq[$iIndex] : false;
  1064.     }
  1065.     /**
  1066.      * Get the token constant for the current Parenthesis
  1067.      * @param int   current token -'x'
  1068.      * @return mixed token constant or false
  1069.      */
  1070.     public function getControlParenthesis($iRet = 0) 
  1071.     {
  1072.         $iIndex = count($this->aControlParenthesis) -$iRet-1;
  1073.         return ($iIndex >= 0) ? $this->aControlParenthesis[$iIndex] : false;
  1074.     }
  1075.     ////
  1076.     // Mode methods
  1077.     ////
  1078.     
  1079.     /**
  1080.      * Set a mode to true
  1081.      * @param string name of the mode
  1082.      */
  1083.     public function setMode($sKey) 
  1084.     {
  1085.         $this->aModes[$sKey] = true;
  1086.     }
  1087.     /**
  1088.      * Set a mode to false
  1089.      * @param string name of the mode
  1090.      */
  1091.     public function unsetMode($sKey) 
  1092.     {
  1093.         $this->aModes[$sKey] = false;
  1094.     }
  1095.     /**
  1096.      * Get the state of a mode
  1097.      * @param  string name of the mode
  1098.      * @return bool
  1099.      */
  1100.     public function getMode($sKey) 
  1101.     {
  1102.         return array_key_exists($sKey, $this->aModes) ? $this->aModes[$sKey] : false;
  1103.     }
  1104.     /////
  1105.     // Filter methods
  1106.     /////
  1107.     
  1108.     /**
  1109.      * Add a string to the output
  1110.      * @param string
  1111.      */
  1112.     public function add($token) 
  1113.     {
  1114.         $this->aOut[] = $token;
  1115.     }
  1116.     /**
  1117.      * Delete the last added output(s)
  1118.      * @param int number of outputs to drop
  1119.      * @deprecated
  1120.      */
  1121.     public function pop($iReps = 1) 
  1122.     {
  1123.         for ($x = 0 ; $x < $iReps ; $x++) {
  1124.             $sLast = array_pop($this->aOut);
  1125.         }
  1126.         return $sLast;
  1127.     }
  1128.     /**
  1129.      * Add Indent to the output
  1130.      * @see $sIndentChar
  1131.      * @see $iIndentNumber
  1132.      * @see $iIndent
  1133.      */
  1134.     public function addIndent() 
  1135.     {
  1136.         $this->aOut[] = str_repeat($this->sIndentChar, $this->iIndent);
  1137.     }
  1138.     /**
  1139.      * Set a string to put before a new line
  1140.      * You could use this to put a standard comment after some sentences
  1141.      * or to add extra newlines
  1142.      */
  1143.     public function setBeforeNewLine($sText) 
  1144.     {
  1145.         $this->sBeforeNewLine = $sText;
  1146.     }
  1147.     /**
  1148.      * Add a new line to the output
  1149.      * @see $sNewLine
  1150.      */
  1151.     public function addNewLine() 
  1152.     {
  1153.         if (!is_null($this->sBeforeNewLine)) {
  1154.             $this->aOut[] = $this->sBeforeNewLine;
  1155.             $this->sBeforeNewLine = null;
  1156.         }
  1157.         $this->aOut[] = $this->sNewLine;
  1158.     }
  1159.     /**
  1160.      * Add a new line and a indent to output
  1161.      * @see $sIndentChar
  1162.      * @see $iIndentNumber
  1163.      * @see $iIndent
  1164.      * @see $sNewLine
  1165.      */
  1166.     public function addNewLineIndent() 
  1167.     {
  1168.         if (!is_null($this->sBeforeNewLine)) {
  1169.             $this->aOut[] = $this->sBeforeNewLine;
  1170.             $this->sBeforeNewLine = null;
  1171.         }
  1172.         $this->aOut[] = $this->sNewLine;
  1173.         $this->aOut[] = str_repeat($this->sIndentChar, $this->iIndent);
  1174.     }
  1175.     /**
  1176.      * Increments the indent in X chars.
  1177.      * If param omitted, used {@link iIndentNumber }
  1178.      * @param    int increment indent in x chars
  1179.      */
  1180.     public function incIndent($iIncr = false) 
  1181.     {
  1182.         if (!$iIncr) {
  1183.             $iIncr = $this->iIndentNumber;
  1184.         }
  1185.         array_push($this->aIndentStack, $iIncr);
  1186.         $this->iIndent+= $iIncr;
  1187.     }
  1188.     /**
  1189.      * Decrements the indent.
  1190.      */
  1191.     public function decIndent() 
  1192.     {
  1193.         if (count($this->aIndentStack > 1)) {
  1194.             $iLastIndent = array_pop($this->aIndentStack);
  1195.             $this->iIndent-= $iLastIndent;
  1196.         }
  1197.     }
  1198.     //
  1199.     ////
  1200.     // Methods to lookup previous, next tokens
  1201.     ////
  1202.     //
  1203.     
  1204.     /**
  1205.      * Get the 'x' significant (non whitespace)previous token
  1206.      * @param  int   current-x token
  1207.      * @return mixed array or false
  1208.      */
  1209.     private function getPreviousToken($iPrev = 1) 
  1210.     {
  1211.         for ($x = $this->iCount-1 ; $x >= 0 ; $x--) {
  1212.             $aToken = &$this->getToken($x);
  1213.             if ($aToken[0] != T_WHITESPACE) {
  1214.                 $iPrev--;
  1215.                 if (!$iPrev) {
  1216.                     return $aToken;
  1217.                 }
  1218.             }
  1219.         }
  1220.     }
  1221.     /**
  1222.      * Get the 'x' significant (non whitespace) next token
  1223.      * @param  int   current+x token
  1224.      * @return array
  1225.      */
  1226.     private function getNextToken($iNext = 1) 
  1227.     {
  1228.         for ($x = $this->iCount+1 ; $x < (count($this->aTokens) -1) ; $x++) {
  1229.             $aToken = &$this->getToken($x);
  1230.             if ($aToken[0] != T_WHITESPACE) {
  1231.                 $iNext--;
  1232.                 if (!$iNext) {
  1233.                     return $aToken;
  1234.                 }
  1235.             }
  1236.         }
  1237.     }
  1238.     /**
  1239.      * Return true if any of the constant defined is param 1 is the previous 'x' constant
  1240.      * @param    mixed int (constant) or array of constants
  1241.      * @return   bool
  1242.      */
  1243.     public function isPreviousTokenConstant($mValue, $iPrev = 1) 
  1244.     {
  1245.         if (!is_array($mValue)) {
  1246.             $mValue = array(
  1247.                 $mValue
  1248.             );
  1249.         }
  1250.         $iPrevious = $this->getPreviousTokenConstant($iPrev);
  1251.         return in_array($iPrevious, $mValue);
  1252.     }
  1253.     /**
  1254.      * Return true if any of the content defined is param 1 is the previous 'x' content
  1255.      * @param    mixed string (content) or array of contents
  1256.      * @return   bool
  1257.      */
  1258.     public function isPreviousTokenContent($mValue, $iPrev = 1) 
  1259.     {
  1260.         if (!is_array($mValue)) {
  1261.             $mValue = array(
  1262.                 $mValue
  1263.             );
  1264.         }
  1265.         $iPrevious = $this->getPreviousTokenContent($iPrev);
  1266.         return in_array($iPrevious, $mValue);
  1267.     }
  1268.     /**
  1269.      * Return true if any of the constant defined in param 1 is the next 'x' content
  1270.      * @param    mixed int (constant) or array of constants
  1271.      * @return   bool
  1272.      */
  1273.     public function isNextTokenConstant($mValue, $iPrev = 1) 
  1274.     {
  1275.         if (!is_array($mValue)) {
  1276.             $mValue = array(
  1277.                 $mValue
  1278.             );
  1279.         }
  1280.         $iNext = $this->getNextTokenConstant($iPrev);
  1281.         return in_array($iNext, $mValue);
  1282.     }
  1283.     /**
  1284.      * Return true if any of the content defined is param 1 is the next 'x' content
  1285.      * @param    mixed string (content) or array of contents
  1286.      * @return   bool
  1287.      */
  1288.     public function isNextTokenContent($mValue, $iPrev = 1) 
  1289.     {
  1290.         if (!is_array($mValue)) {
  1291.             $mValue = array(
  1292.                 $mValue
  1293.             );
  1294.         }
  1295.         $iNext = $this->getNextTokenContent($iPrev);
  1296.         return in_array($iNext, $mValue);
  1297.     }
  1298.     /**
  1299.      * Get the 'x' significant (non whitespace) previous token constant
  1300.      * @param  int   current-x token
  1301.      * @return int
  1302.      */
  1303.     public function getPreviousTokenConstant($iPrev = 1) 
  1304.     {
  1305.         $sToken = $this->getPreviousToken($iPrev);
  1306.         return $sToken[0];
  1307.     }
  1308.     /**
  1309.      * Get the 'x' significant (non whitespace) previous token text
  1310.      * @param  int   current-x token
  1311.      * @return string
  1312.      */
  1313.     public function getPreviousTokenContent($iPrev = 1) 
  1314.     {
  1315.         $mToken = $this->getPreviousToken($iPrev);
  1316.         return (is_string($mToken)) ? $mToken : $mToken[1];
  1317.     }
  1318.     public function getNextTokenNonCommentConstant($iPrev = 1) 
  1319.     {
  1320.         do {
  1321.             $aToken = $this->getNextToken($iPrev);
  1322.             $iPrev++;
  1323.         }
  1324.         while ($aToken[0] == T_COMMENT);
  1325.         return $aToken[0];
  1326.     }
  1327.     /**
  1328.      * Get the 'x' significant (non whitespace) next token constant
  1329.      * @param  int   current+x token
  1330.      * @return int
  1331.      */
  1332.     public function getNextTokenConstant($iPrev = 1) 
  1333.     {
  1334.         $sToken = $this->getNextToken($iPrev);
  1335.         return $sToken[0];
  1336.     }
  1337.     /**
  1338.      * Get the 'x' significant (non whitespace) next token text
  1339.      * @param  int   current+x token
  1340.      * @return int
  1341.      */
  1342.     public function getNextTokenContent($iNext = 1) 
  1343.     {
  1344.         $mToken = $this->getNextToken($iNext);
  1345.         return (is_string($mToken)) ? $mToken : $mToken[1];
  1346.     }
  1347.     /**
  1348.      * Return the whitespace previous to current token
  1349.      * Ex.: You have
  1350.      * '    if($a)'
  1351.      * if you call this funcion on 'if', you get '    '
  1352.      * @todo implements a more economic way to handle this.
  1353.      * @return   string  previous whitespace
  1354.      */
  1355.     public function getPreviousWhitespace() 
  1356.     {
  1357.         $sWhiteSpace = '';
  1358.         for ($x = $this->iCount-1 ; $x >= 0 ; $x--) {
  1359.             $this->oLog->log("sp n:$x", PEAR_LOG_DEBUG);
  1360.             $aToken = $this->getToken($x);
  1361.             if (is_array($aToken)) {
  1362.                 if ($aToken[0] == T_WHITESPACE) {
  1363.                     $sWhiteSpace.= $aToken[1];
  1364.                 } elseif (preg_match("/([\s\r\n]+)$/", $aToken[1], $aMatch)) {
  1365.                     $sWhiteSpace.= $aMatch[0];
  1366.                     // ArrayNested->off();
  1367.                     $this->oLog->log("+space-token-with-sp:[" . PHP_Beautifier_Common::wsToString($sWhiteSpace) . "]", PEAR_LOG_DEBUG);
  1368.                     // ArrayNested->on();
  1369.                     return $sWhiteSpace;
  1370.                 }
  1371.             } else {
  1372.                 $this->oLog->log("+space-token-without-sp:[" . PHP_Beautifier_Common::wsToString($sWhiteSpace) . "]", PEAR_LOG_DEBUG);
  1373.                 return $sWhiteSpace;
  1374.             }
  1375.         }
  1376.         // Strange, but...
  1377.         $this->oLog->log("+space:[" . PHP_Beautifier_Common::wsToString($sWhiteSpace) . "]", PEAR_LOG_DEBUG);
  1378.         return $sWhiteSpace;
  1379.     }
  1380.     /**
  1381.      * Remove all whitespace from the previous tag
  1382.      * @return bool  false if previous token was short comment or heredoc
  1383.      *               (don't remove ws)
  1384.      *               true anything else.
  1385.      */
  1386.     public function removeWhitespace() 
  1387.     {
  1388.         // if the previous token was
  1389.         // - a short comment
  1390.         // - heredoc
  1391.         // don't remove whitespace!
  1392.         //
  1393.         if ($this->isPreviousTokenConstant(T_COMMENT) and preg_match("/^(\/\/|#)/", $this->getPreviousTokenContent())) { // Here for short comment
  1394.             return false;
  1395.         } elseif ($this->getPreviousTokenConstant(2) == T_END_HEREDOC) { // And here for heredoc
  1396.             return false;
  1397.         }
  1398.         $pop = 0;
  1399.         for ($i = count($this->aOut) -1 ; $i >= 0 ; $i--) { // go backwards
  1400.             $cNow = &$this->aOut[$i];
  1401.             if (strlen(trim($cNow)) == 0) { // only space
  1402.                 array_pop($this->aOut); // delete it!
  1403.                 $pop++;
  1404.             } else { // we find something!
  1405.                 $cNow = rtrim($cNow); // rtrim out
  1406.                 break;
  1407.             }
  1408.         }
  1409.         $this->oLog->log("-space $pop", PEAR_LOG_DEBUG);
  1410.         return true;
  1411.     }
  1412.     /**
  1413.      * Get a token by number
  1414.      * @param int number of the token
  1415.      * @return array
  1416.      */
  1417.     public function &getToken($iIndex) 
  1418.     {
  1419.         if ($iIndex < 0 or $iIndex > count($this->aTokens)) {
  1420.             return false;
  1421.         } else {
  1422.             return $this->aTokens[$iIndex];
  1423.         }
  1424.     }
  1425.     public function openBraceDontProcess() {
  1426.         return $this->isPreviousTokenConstant(T_VARIABLE) or $this->isPreviousTokenConstant(T_OBJECT_OPERATOR) or ($this->isPreviousTokenConstant(T_STRING) and $this->getPreviousTokenConstant(2) == T_OBJECT_OPERATOR) or $this->getMode('double_quote');
  1427.     }
  1428. }
  1429. ?>