commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / packages / PHP / Beautifier.php
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 ?>