2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 * Contents Php_Beautifier class and make some tests
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.
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
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.");
27 include_once 'PEAR.php';
28 include_once 'PEAR/Exception.php';
30 * Require PHP_Beautifier_Filter
32 include_once 'Beautifier/Filter.php';
34 * Require PHP_Beautifier_Filter_Default
36 include_once 'Beautifier/Filter/Default.filter.php';
38 * Require PHP_Beautifier_Common
40 include_once 'Beautifier/Common.php';
44 include_once 'Log.php';
48 include_once 'Beautifier/Exception.php';
50 * Require StreamWrapper
52 // include_once 'Beautifier/StreamWrapper.php';
56 * Class to beautify php code
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)
62 * # Get it, save it or show it.
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
72 * @todo create a web interface.
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
82 class PHP_Beautifier
implements PHP_Beautifier_Interface
87 * Tokens created by the tokenizer
90 public $aTokens = array();
92 * Tokens codes assigned to method on Filter
95 public $aTokenFunctions = array();
100 public $aTokenNames = Array();
105 public $aOut = array();
107 * Contains the assigment of modes
113 public $aModes = array();
115 * List of availables modes
118 public $aModesAvailable = array(
123 * Settings for the class
126 public $aSettings = array();
128 * Index of current token
133 * Chars for indentation
136 public $iIndentNumber = 4;
138 * Level of array nesting
143 * Level of ternary operator nesting
146 public $iTernary = 0;
148 * Level of parenthesis nesting
151 public $iParenthesis = 0;
153 * Level of verbosity (debug)
156 public $iVerbose = false;
161 public $sInputFile = '';
163 * Name of output file
166 public $sOutputFile = '';
171 public $sNewLine = PHP_EOL
;
173 * Type of whitespace to use for indent
176 public $sIndentChar = ' ';
178 * Save the last whitespace used. Use only for Filter! (i miss friends of C++ :( )
181 public $currentWhitespace = '';
183 * Association $aTokens=>$aOut
186 public $aAssocs = array();
192 private $sFileType = 'php';
197 private $iIndent = 0;
201 private $aIndentStack = array();
202 /** Text to beautify */
204 /** Constant for last Control */
205 private $iControlLast;
206 /** References to PHP_Beautifier_Filter's */
207 private $aFilters = array();
209 * Stack with control construct
211 private $aControlSeq = array();
213 * List of construct that start control structures
215 private $aControlStructures = array();
217 * List of Control for parenthesis
219 private $aControlParenthesis = array();
221 * List of construct that end control structures
223 private $aControlStructuresEnd = array();
224 /** Dirs for Filters */
225 private $aFilterDirs = array();
226 /** Flag for beautify/no beautify mode */
227 private $bBeautify = true;
230 /** Before new line holder */
231 private $sBeforeNewLine = null;
232 /** Activate or deactivate 'no delete previous space' */
233 private $bNdps = false;
238 * Assing values to {@link $aControlStructures},{@link $aControlStructuresEnd},
239 * {@link $aTokenFunctions}
241 public function __construct()
243 $this->aControlStructures
= array(
258 $this->aControlStructuresEnd
= array(
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;
271 $aTokensToChange = array(
273 '"' => "T_DOUBLE_QUOTE",
274 "'" => "T_SINGLE_QUOTE",
276 '(' => 'T_PARENTHESIS_OPEN',
277 ')' => 'T_PARENTHESIS_CLOSE',
278 ';' => 'T_SEMI_COLON',
279 '{' => 'T_OPEN_BRACE',
280 '}' => 'T_CLOSE_BRACE',
284 '=' => 'T_ASSIGMENT',
288 '[' => 'T_OPEN_SQUARE_BRACE',
289 ']' => 'T_CLOSE_SQUARE_BRACE',
300 T_SL
=> 'T_OPERATOR',
301 T_SR
=> 'T_OPERATOR',
302 T_OBJECT_OPERATOR
=> 'T_OBJECT_OPERATOR',
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',
321 T_WHILE
=> 'T_CONTROL',
322 T_SWITCH
=> 'T_CONTROL',
324 T_ELSEIF
=> 'T_ELSE',
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',
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',
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',
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',
368 foreach($aTokensToChange as $iToken => $sFunction) {
369 $this->aTokenFunctions
[$iToken] = $sFunction;
371 $this->addFilterDirectory(dirname(__FILE__
) . '/Beautifier/Filter');
372 $this->addFilter('Default');
373 $this->oLog
= PHP_Beautifier_Common
::getLog();
375 public function getTokenName($iToken)
378 throw new Exception("Token $iToken doesn't exists");
380 return $this->aTokenNames
[$iToken];
383 * Start the log for debug
384 * @param string filename
385 * @param int debug level. See {@link Log}
387 public function startLog($sFile = 'php_beautifier.log', $iLevel = PEAR_LOG_DEBUG
)
390 $oLogFile = Log
::factory('file', $sFile, 'php_beautifier', array() , PEAR_LOG_DEBUG
);
391 $this->oLog
->addChild($oLogFile);
394 * Add a filter directory
395 * @param string path to directory
398 public function addFilterDirectory($sDir)
400 $sDir = PHP_Beautifier_Common
::normalizeDir($sDir);
401 if (file_exists($sDir)) {
402 array_push($this->aFilterDirs
, $sDir);
404 throw new Exception_PHP_Beautifier_Filter("Path '$sDir' doesn't exists");
408 * Return an array with all the Filter Dirs
409 * @return array List of Filter Directories
411 public function getFilterDirectories()
413 return $this->aFilterDirs
;
415 private function addFilterObject(PHP_Beautifier_Filter
$oFilter)
417 array_unshift($this->aFilters
, $oFilter);
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
429 public function addFilter($mFilter, $aSettings = array())
431 if ($mFilter instanceOf PHP_Beautifier_Filter
) {
432 return $this->addFilterObject($mFilter);
434 $sFilterClass = 'PHP_Beautifier_Filter_' . $mFilter;
435 if (!class_exists($sFilterClass)) {
436 $this->addFilterFile($mFilter);
438 $oTemp = new $sFilterClass($this, $aSettings);
439 // verify if same Filter is loaded
440 if (in_array($oTemp, $this->aFilters
, TRUE)) {
442 } elseif ($oTemp instanceof PHP_Beautifier_Filter
) {
443 $this->addFilterObject($oTemp);
445 throw new Exception_PHP_Beautifier_Filter("'$sFilterClass' isn't a subclass of 'Filter'");
450 * @param string name of the filter
451 * @return bool true if Filter is removed, false otherwise
453 public function removeFilter($sFilter)
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]);
465 * Return the Filter Description
466 * @see PHP_Beautifier_Filter::__toString();
467 * @param string name of the filter
468 * @return mixed string or false
470 public function getFilterDescription($sFilter)
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());
483 * Add a new filter to the processor.
484 * The system will process the filter in LIFO order
485 * @param string name of filter
490 private function addFilterFile($sFilter)
492 $sFilterClass = 'PHP_Beautifier_Filter_' . $sFilter;
493 if (class_exists($sFilterClass)) {
496 foreach($this->aFilterDirs
as $sDir) {
497 $sFile = $sDir . $sFilter . '.filter.php';
498 if (file_exists($sFile)) {
500 if (class_exists($sFilterClass)) {
503 throw new Exception_PHP_Beautifier_Filter("File '$sFile' exists,but doesn't exists filter '$sFilterClass'");
507 throw new Exception_PHP_Beautifier_Filter("Doesn't exists filter '$sFilter'");
510 * Get the names of the loaded filters
511 * @return array list of Filters
513 public function getFilterList()
515 foreach($this->aFilters
as $oFilter) {
516 $aOut[] = $oFilter->getName();
521 * Get the list of all available Filters in all the include Dirs
522 * @return array list of Filters
524 public function getFilterListTotal()
526 $aFilterFiles = array();
527 foreach($this->aFilterDirs
as $sDir) {
528 $aFiles = PHP_Beautifier_Common
::getFilesByPattern($sDir, ".*?\.filter\.php");
529 array_walk($aFiles, array(
531 'getFilterList_FilterName'
533 $aFilterFiles = array_merge($aFilterFiles, $aFiles);
536 return $aFilterFiles;
539 * Receive a path to a filter and replace it with the name of filter
541 private function getFilterList_FilterName(&$sFile)
543 preg_match("/\/([^\/]*?)\.filter\.php/", $sFile, $aMatch);
546 public function getIndentChar()
548 return $this->sIndentChar
;
550 public function getIndentNumber()
552 return $this->iIndentNumber
;
554 public function getNewLine()
556 return $this->sNewLine
;
559 * Character used for indentation
560 * @param string usually ' ' or "\t"
562 public function setIndentChar($sChar)
564 $this->sIndentChar
= $sChar;
567 * Number of characters for indentation
568 * @param int ussualy 4 for space or 1 for tabs
570 public function setIndentNumber($iIndentNumber)
572 $this->iIndentNumber
= $iIndentNumber;
575 * Character used as a new line
576 * @param string ussualy "\n", "\r\n" or "\r"
578 public function setNewLine($sNewLine)
580 $this->sNewLine
= $sNewLine;
583 * Set the file for beautify
584 * @param string path to file
587 public function setInputFile($sFile)
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");
594 $this->sInputFile
= $sFile;
595 $fp = ($bCli and $sFile == STDIN
) ? STDIN
: fopen($sFile, 'r');
597 $data = fread($fp, 8192);
598 if (strlen($data) == 0) {
601 $this->sText
.= $data;
604 if (!($bCli and $fp == STDIN
)) {
610 * Set the output file for beautify
611 * @param string path to file
613 public function setOutputFile($sFile)
615 $this->sOutputFile
= $sFile;
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();
623 public function save($sFile = null)
625 $bCli = (php_sapi_name() == 'cli');
627 if (!$this->sOutputFile
) {
628 throw new Exception("Can't save without a output file");
630 $sFile = $this->sOutputFile
;
633 $sText = $this->get();
634 $fp = ($bCli and $sFile == STDOUT
) ? STDOUT
: @fopen
($sFile, "w");
636 throw new Exception("Can't save file $sFile");
638 fputs($fp, $sText, strlen($sText));
639 if (!($bCli and $sFile == STDOUT
)) {
642 $this->oLog
->log("Success: $sFile saved", PEAR_LOG_INFO
);
646 * Set a string for beautify
647 * @param string Must be preceded by open tag
649 public function setInputString($sText)
651 $this->sText
= $sText;
654 * Reset all properties
656 private function resetProperties()
658 $aProperties = array(
659 'aTokens' => array() ,
661 'aModes' => array() ,
664 /*$this->iIndentNumber*/
666 'aIndentStack' => array(
667 /*$this->iIndentNumber*/
671 'currentWhitespace' => '',
672 'aAssocs' => array() ,
673 'iControlLast' => null,
674 'aControlSeq' => array() ,
677 foreach($aProperties as $sProperty => $sValue) {
678 $this->$sProperty = $sValue;
682 * Process the string or file to beautify
683 * @return bool true on success
686 public function process()
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
694 if ($this->sFileType
== 'php') {
695 $this->aTokens
= token_get_all($this->sText
);
697 $sClass = 'PHP_Beautifier_Tokeniker_' . ucfirst($this->sFileType
);
698 if (class_exists($sClass)) {
699 $oTokenizer = new $sClass($this->sText
);
700 $this->aTokens
= $oTokenizer->getTokens();
702 throw new Exception("File type " . $this->sFileType
. " not implemented");
705 $this->aOut
= array();
706 $iTotal = count($this->aTokens
);
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();
712 for ($this->iCount
= 0 ; $this->iCount
< $iTotal ; $this->iCount++
) {
713 $aCurrentToken = $this->aTokens
[$this->iCount
];
714 if (is_string($aCurrentToken)) {
715 $aCurrentToken = array(
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
728 if ($this->bBeautify
) {
729 foreach($this->aFilters
as $oFilter) {
731 if ($oFilter->handleToken($aCurrentToken) !== FALSE) {
732 $this->oLog
->log('Filter:' . $oFilter->getName() , PEAR_LOG_DEBUG
);
738 $this->add($aCurrentToken[1]);
740 $this->controlTokenPost($aCurrentToken);
741 $iLastOut = count($this->aOut
);
743 if (($iLastOut-$iFirstOut) > 0) {
744 $this->aAssocs
[$this->iCount
] = array(
745 'offset' => $iFirstOut
747 if ($iPrevAssoc !== FALSE) {
748 $this->aAssocs
[$iPrevAssoc]['length'] = $iFirstOut-$this->aAssocs
[$iPrevAssoc]['offset'];
750 $iPrevAssoc = $this->iCount
;
753 throw new Exception("Can'process token: " . var_dump($aCurrentToken));
756 // generate the last assoc
757 if (count($this->aOut
) == 0) {
759 throw new Exception("Nothing on output for " . $this->sFile
. "!");
761 throw new Exception("Nothing on output!");
764 $this->aAssocs
[$iPrevAssoc]['length'] = (count($this->aOut
) -1) -$this->aAssocs
[$iPrevAssoc]['offset'];
766 foreach($this->aFilters
as $oFilter) {
767 $oFilter->postProcess();
769 $this->oLog
->log('End process', PEAR_LOG_DEBUG
);
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
777 public function getTokenAssoc($iIndex)
779 return (array_key_exists($iIndex, $this->aAssocs
)) ?
$this->aAssocs
[$iIndex] : false;
782 * Get the output for the specified token
783 * @param int token number
784 * @return mixed string or false if token doesn't exists
786 public function getTokenAssocText($iIndex)
788 if (!($aAssoc = $this->getTokenAssoc($iIndex))) {
791 return (implode('', array_slice($this->aOut
, $aAssoc['offset'], $aAssoc['length'])));
794 * Replace the output for specified token
795 * @param int token number
796 * @param string replace text
799 public function replaceTokenAssoc($iIndex, $sText)
801 if (!($aAssoc = $this->getTokenAssoc($iIndex))) {
804 $this->aOut
[$aAssoc['offset']] = $sText;
805 for ($x = 0 ; $x < $aAssoc['length']-1 ; $x++
) {
806 $this->aOut
[$aAssoc['offset']+
$x+
1] = '';
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
815 public function getTokenFunction($sTokenType)
817 return (array_key_exists($sTokenType, $this->aTokenFunctions
)) ?
strtolower($this->aTokenFunctions
[$sTokenType]) : false;
820 * Process a callback from the code to beautify
821 * @param array third parameter from preg_match
823 * @uses controlToken()
825 private function processCallback($aMatch)
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] . ";");
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] . ";");
843 * Assign value for some variables with the information of the token
844 * @param array current token
846 private function controlToken($aCurrentToken)
848 // is a control structure opener?
849 if (in_array($aCurrentToken[0], $this->aControlStructures
)) {
850 $this->pushControlSeq($aCurrentToken);
851 $this->iControlLast
= $aCurrentToken[0];
853 // is a control structure closer?
854 if (in_array($aCurrentToken[0], $this->aControlStructuresEnd
)) {
855 $this->popControlSeq();
857 switch ($aCurrentToken[0]) {
860 if (preg_match("/\/\/\s*(.*?)->((.*)\((.*)\))/", $aCurrentToken[1], $aMatch)) {
862 $this->processCallback($aMatch);
864 catch(Exception
$oExp) {
870 $this->setMode('function');
874 $this->setMode('class');
882 $this->currentWhitespace
= $aCurrentToken[1];
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');
892 $this->iParenthesis++
;
893 $this->pushControlParenthesis();
897 $this->iParenthesis
--;
901 $this->setMode('ternary_operator');
906 ($this->getMode('double_quote')) ?
$this->unsetMode('double_quote') : $this->setMode('double_quote');
909 case T_START_HEREDOC
:
910 $this->setMode('double_quote');
914 $this->unsetMode('double_quote');
917 if ($this->getTokenFunction($aCurrentToken[0]) == 't_include') {
918 $this->setMode('include');
922 * Assign value for some variables with the information of the token, after processing
923 * @param array current token
925 private function controlTokenPost($aCurrentToken)
927 switch ($aCurrentToken[0]) {
932 $this->popControlParenthesis();
936 if ($this->getMode('string_index')) {
937 $this->unsetMode('string_index');
939 $this->oLog
->log('end bracket:' . $this->getPreviousTokenContent() , PEAR_LOG_DEBUG
);
940 if ($this->getPreviousTokenContent() == ';' or $this->getPreviousTokenContent() == '}' or $this->getPreviousTokenContent() == '{') {
941 $this->popControlSeq();
947 $this->unsetMode('function');
950 if ($this->getTokenFunction($aCurrentToken[0]) == 't_colon') {
951 if ($this->iTernary
) {
954 if(!$this->iTernary
) {
955 $this->unsetMode('ternary_operator');
960 * Push a control construct to the stack
961 * @param array current token
963 private function pushControlSeq($aToken)
965 $this->oLog
->log('Push Control:' . $aToken[0] . "->" . $aToken[1], PEAR_LOG_DEBUG
);
966 array_push($this->aControlSeq
, $aToken[0]);
969 * Pop a control construct from the stack
970 * @return int token constant
972 private function popControlSeq()
974 $aEl = array_pop($this->aControlSeq
);
975 $this->oLog
->log('Pop Control:' . $this->getTokenName($aEl) , PEAR_LOG_DEBUG
);
979 * Push a new Control Instruction on the stack
981 private function pushControlParenthesis()
983 $iPrevious = $this->getPreviousTokenConstant();
984 $this->oLog
->log("Push Parenthesis: $iPrevious ->" . $this->getPreviousTokenContent() , PEAR_LOG_DEBUG
);
985 array_push($this->aControlParenthesis
, $iPrevious);
988 * Pop the last Control instruction for parenthesis from the stack
989 * @return int constant
991 private function popControlParenthesis()
993 $iPop = array_pop($this->aControlParenthesis
);
994 $this->oLog
->log('Pop Parenthesis:' . $iPop, PEAR_LOG_DEBUG
);
1001 public function setFileType($sType)
1003 $this->sFileType
= $sType;
1006 * Set the Beautifier on or off
1009 public function setBeautify($sFlag)
1011 $this->bBeautify
= (bool)$sFlag;
1014 * Show the beautified code
1016 public function show()
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
1029 function setNoDeletePreviousSpaceHack($bFlag = true)
1031 $this->bNdps
= $bFlag;
1034 * Returns the beautified code
1035 * @see setNoDeletePreviousSpaceHack()
1038 public function get()
1040 if (!$this->bNdps
) {
1041 return implode('', $this->aOut
);
1043 return str_replace('/**ndps**/', '', implode('', $this->aOut
));
1047 * Returns the value of a settings
1048 * @param string Name of the setting
1049 * @return mixed Value of the settings or false
1051 public function getSetting($sKey)
1053 return (array_key_exists($sKey, $this->aSettings
)) ?
$this->aSettings
[$sKey] : false;
1056 * Get the token constant for the current control construct
1057 * @param int current token -'x'
1058 *@ return mixed token constant or false
1060 public function getControlSeq($iRet = 0)
1062 $iIndex = count($this->aControlSeq
) -$iRet-1;
1063 return ($iIndex >= 0) ?
$this->aControlSeq
[$iIndex] : false;
1066 * Get the token constant for the current Parenthesis
1067 * @param int current token -'x'
1068 * @return mixed token constant or false
1070 public function getControlParenthesis($iRet = 0)
1072 $iIndex = count($this->aControlParenthesis
) -$iRet-1;
1073 return ($iIndex >= 0) ?
$this->aControlParenthesis
[$iIndex] : false;
1080 * Set a mode to true
1081 * @param string name of the mode
1083 public function setMode($sKey)
1085 $this->aModes
[$sKey] = true;
1088 * Set a mode to false
1089 * @param string name of the mode
1091 public function unsetMode($sKey)
1093 $this->aModes
[$sKey] = false;
1096 * Get the state of a mode
1097 * @param string name of the mode
1100 public function getMode($sKey)
1102 return array_key_exists($sKey, $this->aModes
) ?
$this->aModes
[$sKey] : false;
1109 * Add a string to the output
1112 public function add($token)
1114 $this->aOut
[] = $token;
1117 * Delete the last added output(s)
1118 * @param int number of outputs to drop
1121 public function pop($iReps = 1)
1123 for ($x = 0 ; $x < $iReps ; $x++
) {
1124 $sLast = array_pop($this->aOut
);
1129 * Add Indent to the output
1131 * @see $iIndentNumber
1134 public function addIndent()
1136 $this->aOut
[] = str_repeat($this->sIndentChar
, $this->iIndent
);
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
1143 public function setBeforeNewLine($sText)
1145 $this->sBeforeNewLine
= $sText;
1148 * Add a new line to the output
1151 public function addNewLine()
1153 if (!is_null($this->sBeforeNewLine
)) {
1154 $this->aOut
[] = $this->sBeforeNewLine
;
1155 $this->sBeforeNewLine
= null;
1157 $this->aOut
[] = $this->sNewLine
;
1160 * Add a new line and a indent to output
1162 * @see $iIndentNumber
1166 public function addNewLineIndent()
1168 if (!is_null($this->sBeforeNewLine
)) {
1169 $this->aOut
[] = $this->sBeforeNewLine
;
1170 $this->sBeforeNewLine
= null;
1172 $this->aOut
[] = $this->sNewLine
;
1173 $this->aOut
[] = str_repeat($this->sIndentChar
, $this->iIndent
);
1176 * Increments the indent in X chars.
1177 * If param omitted, used {@link iIndentNumber }
1178 * @param int increment indent in x chars
1180 public function incIndent($iIncr = false)
1183 $iIncr = $this->iIndentNumber
;
1185 array_push($this->aIndentStack
, $iIncr);
1186 $this->iIndent+
= $iIncr;
1189 * Decrements the indent.
1191 public function decIndent()
1193 if (count($this->aIndentStack
> 1)) {
1194 $iLastIndent = array_pop($this->aIndentStack
);
1195 $this->iIndent
-= $iLastIndent;
1200 // Methods to lookup previous, next tokens
1205 * Get the 'x' significant (non whitespace)previous token
1206 * @param int current-x token
1207 * @return mixed array or false
1209 private function getPreviousToken($iPrev = 1)
1211 for ($x = $this->iCount
-1 ; $x >= 0 ; $x--) {
1212 $aToken = &$this->getToken($x);
1213 if ($aToken[0] != T_WHITESPACE
) {
1222 * Get the 'x' significant (non whitespace) next token
1223 * @param int current+x token
1226 private function getNextToken($iNext = 1)
1228 for ($x = $this->iCount+
1 ; $x < (count($this->aTokens
) -1) ; $x++
) {
1229 $aToken = &$this->getToken($x);
1230 if ($aToken[0] != T_WHITESPACE
) {
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
1243 public function isPreviousTokenConstant($mValue, $iPrev = 1)
1245 if (!is_array($mValue)) {
1250 $iPrevious = $this->getPreviousTokenConstant($iPrev);
1251 return in_array($iPrevious, $mValue);
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
1258 public function isPreviousTokenContent($mValue, $iPrev = 1)
1260 if (!is_array($mValue)) {
1265 $iPrevious = $this->getPreviousTokenContent($iPrev);
1266 return in_array($iPrevious, $mValue);
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
1273 public function isNextTokenConstant($mValue, $iPrev = 1)
1275 if (!is_array($mValue)) {
1280 $iNext = $this->getNextTokenConstant($iPrev);
1281 return in_array($iNext, $mValue);
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
1288 public function isNextTokenContent($mValue, $iPrev = 1)
1290 if (!is_array($mValue)) {
1295 $iNext = $this->getNextTokenContent($iPrev);
1296 return in_array($iNext, $mValue);
1299 * Get the 'x' significant (non whitespace) previous token constant
1300 * @param int current-x token
1303 public function getPreviousTokenConstant($iPrev = 1)
1305 $sToken = $this->getPreviousToken($iPrev);
1309 * Get the 'x' significant (non whitespace) previous token text
1310 * @param int current-x token
1313 public function getPreviousTokenContent($iPrev = 1)
1315 $mToken = $this->getPreviousToken($iPrev);
1316 return (is_string($mToken)) ?
$mToken : $mToken[1];
1318 public function getNextTokenNonCommentConstant($iPrev = 1)
1321 $aToken = $this->getNextToken($iPrev);
1324 while ($aToken[0] == T_COMMENT
);
1328 * Get the 'x' significant (non whitespace) next token constant
1329 * @param int current+x token
1332 public function getNextTokenConstant($iPrev = 1)
1334 $sToken = $this->getNextToken($iPrev);
1338 * Get the 'x' significant (non whitespace) next token text
1339 * @param int current+x token
1342 public function getNextTokenContent($iNext = 1)
1344 $mToken = $this->getNextToken($iNext);
1345 return (is_string($mToken)) ?
$mToken : $mToken[1];
1348 * Return the whitespace previous to current token
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
1355 public function getPreviousWhitespace()
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;
1372 $this->oLog
->log("+space-token-without-sp:[" . PHP_Beautifier_Common
::wsToString($sWhiteSpace) . "]", PEAR_LOG_DEBUG
);
1373 return $sWhiteSpace;
1377 $this->oLog
->log("+space:[" . PHP_Beautifier_Common
::wsToString($sWhiteSpace) . "]", PEAR_LOG_DEBUG
);
1378 return $sWhiteSpace;
1381 * Remove all whitespace from the previous tag
1382 * @return bool false if previous token was short comment or heredoc
1384 * true anything else.
1386 public function removeWhitespace()
1388 // if the previous token was
1389 // - a short comment
1391 // don't remove whitespace!
1393 if ($this->isPreviousTokenConstant(T_COMMENT
) and preg_match("/^(\/\/|#)/", $this->getPreviousTokenContent())) { // Here for short comment
1395 } elseif ($this->getPreviousTokenConstant(2) == T_END_HEREDOC
) { // And here for heredoc
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!
1404 } else { // we find something!
1405 $cNow = rtrim($cNow); // rtrim out
1409 $this->oLog
->log("-space $pop", PEAR_LOG_DEBUG
);
1413 * Get a token by number
1414 * @param int number of the token
1417 public function &getToken($iIndex)
1419 if ($iIndex < 0 or $iIndex > count($this->aTokens
)) {
1422 return $this->aTokens
[$iIndex];
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');