1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2025-01-20 12:46:47 +01:00

Fix problem with indented strings by introducing PrettyPrinter->pSafe

This commit is contained in:
nikic 2011-06-02 22:52:24 +02:00
parent 352cfde568
commit b80f326b6a
4 changed files with 79 additions and 75 deletions

View File

@ -139,7 +139,7 @@ The pretty printer compiles nodes back to PHP code. "Pretty printing" here is ju
name of the process and does not mean that the output is in any way pretty.
$prettyPrinter = new PrettyPrinter_Zend;
echo '<pre>' . htmlspecialchars($prettyPrinter->pStmts($stmts)) . '</pre>';
echo '<pre>' . htmlspecialchars($prettyPrinter->prettyPrint($stmts)) . '</pre>';
For the code mentioned in the above section this should create the output:
@ -151,6 +151,3 @@ For the code mentioned in the above section this should create the output:
Known Issues
============
* When pretty printing strings and InlineHTML those are indented like any other code, thus causing
extra whitespace to be inserted (causes 23 compare fails in test against Symfony Beta 3).

View File

@ -53,7 +53,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
public function pScalar_String(Node_Scalar_String $node) {
return ($node->isBinary ? 'b' : '')
. (Node_Scalar_String::SINGLE_QUOTED === $node->type
? '\'' . addcslashes($node->value, '\'\\') . '\''
? '\'' . $this->pSafe(addcslashes($node->value, '\'\\')) . '\''
: '"' . addcslashes($node->value, "\n\r\t\f\v$\"\\") . '"'
);
}
@ -394,7 +394,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
return 'function ' . ($node->byRef ? '&' : '')
. '(' . $this->pCommaSeparated($node->params) . ')'
. (!empty($node->useVars) ? ' use(' . $this->pCommaSeparated($node->useVars) . ')': '')
. ' {' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. ' {' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pExpr_LambdaFuncUse(Node_Expr_LambdaFuncUse $node) {
@ -436,7 +436,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
public function pStmt_Interface(Node_Stmt_Interface $node) {
return 'interface ' . $node->name
. (!empty($node->extends) ? ' extends ' . $this->pCommaSeparated($node->extends) : '')
. "\n" . '{' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Class(Node_Stmt_Class $node) {
@ -444,7 +444,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. 'class ' . $node->name
. (null !== $node->extends ? ' extends ' . $this->p($node->extends) : '')
. (!empty($node->implements) ? ' implements ' . $this->pCommaSeparated($node->implements) : '')
. "\n" . '{' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Property(Node_Stmt_Property $node) {
@ -461,7 +461,7 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pCommaSeparated($node->params) . ')'
. (null !== $node->stmts
? "\n" . '{' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}'
? "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}'
: ';');
}
@ -475,8 +475,8 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
public function pStmt_Func(Node_Stmt_Func $node) {
return 'function ' . ($node->byRef ? '&' : '') . $node->name
. '(' . $this->pCommaSeparated($node->params) . ')' . "\n" . '{' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
. '(' . $this->pCommaSeparated($node->params) . ')'
. "\n" . '{' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_FuncParam(Node_Stmt_FuncParam $node) {
@ -497,20 +497,19 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
// Control flow
public function pStmt_If(Node_Stmt_If $node) {
return 'if (' . $this->p($node->cond) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}'
return 'if (' . $this->p($node->cond) . ') {'
. "\n" . $this->pStmts($node->stmts) . "\n" . '}'
. $this->pImplode($node->elseifList)
. (null !== $node->else ? $this->p($node->else) : '');
}
public function pStmt_Elseif(Node_Stmt_Elseif $node) {
return ' elseif (' . $this->p($node->cond) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
return ' elseif (' . $this->p($node->cond) . ') {'
. "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Else(Node_Stmt_Else $node) {
return ' else {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
return ' else {' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_For(Node_Stmt_For $node) {
@ -518,44 +517,44 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
. $this->pCommaSeparated($node->init) . ';'
. $this->pCommaSeparated($node->cond) . ';'
. $this->pCommaSeparated($node->loop)
. ') {' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. ') {' . "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Foreach(Node_Stmt_Foreach $node) {
return 'foreach (' . $this->p($node->expr) . ' as '
. (null !== $node->keyVar ? $this->p($node->keyVar) . ' => ' : '')
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {' . "\n"
. $this->pIndent($this->pStmts($node->stmts)) . '}';
. ($node->byRef ? '&' : '') . $this->p($node->valueVar) . ') {'
. "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_While(Node_Stmt_While $node) {
return 'while (' . $this->p($node->cond) . ') {'
. "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Do(Node_Stmt_Do $node) {
return 'do {' . "\n" . $this->pIndent($this->pStmts($node->stmts))
. '} while (' . $this->p($node->cond) . ')';
return 'do {' . "\n" . $this->pStmts($node->stmts)
. '} while (' . $this->p($node->cond) . "\n" . ')';
}
public function pStmt_Switch(Node_Stmt_Switch $node) {
return 'switch (' . $this->p($node->cond) . ') {'
. "\n" . $this->pIndent($this->pImplode($node->caseList)) . '}';
. "\n" . $this->pImplode($node->caseList) . '}';
}
public function pStmt_TryCatch(Node_Stmt_TryCatch $node) {
return 'try {' . "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}'
return 'try {' . "\n" . $this->pStmts($node->stmts) . "\n" . '}'
. $this->pImplode($node->catches);
}
public function pStmt_Catch(Node_Stmt_Catch $node) {
return ' catch (' . $this->p($node->type) . ' $' . $node->var . ') {'
. "\n" . $this->pIndent($this->pStmts($node->stmts)) . '}';
. "\n" . $this->pStmts($node->stmts) . "\n" . '}';
}
public function pStmt_Case(Node_Stmt_Case $node) {
return (null !== $node->cond ? 'case ' . $this->p($node->cond) : 'default') . ':'
. "\n" . $this->pIndent($this->pStmts($node->stmts));
. "\n" . $this->pStmts($node->stmts) . "\n";
}
public function pStmt_Break(Node_Stmt_Break $node) {
@ -606,7 +605,8 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
}
public function pStmt_InlineHTML(Node_Stmt_InlineHTML $node) {
return '?>' . ("\n" === $node->value[0] ? "\n" : '') . $node->value . '<?php ';
return '?>' . ("\n" === $node->value[0] || "\r" === $node->value[0] ? "\n" : '')
. $this->pSafe($node->value) . '<?php ';
}
// Helpers
@ -643,14 +643,6 @@ class PrettyPrinter_Zend extends PrettyPrinterAbstract
foreach ($encapsList as $i => $element) {
if (is_string($element)) {
$return .= addcslashes($element, "\n\r\t\f\v$\"\\");
} elseif ($element instanceof Node_Variable
&& is_string($element->name)
&& (!isset($encapsList[$i + 1])
|| !is_string($encapsList[$i + 1])
|| '[' !== $encapsList[$i + 1][0]
)
) {
$return .= '$' . $element->name;
} else {
$return .= '{' . $this->p($element) . '}';
}

View File

@ -56,40 +56,62 @@ abstract class PrettyPrinterAbstract
'Expr_LogicalXor' => 17,
'Expr_LogicalOr' => 18,
);
protected $stmtsWithoutSemicolon = array(
'Stmt_Func' => true,
'Stmt_Interface' => true,
'Stmt_Class' => true,
'Stmt_ClassMethod' => true,
'Stmt_For' => true,
'Stmt_Foreach' => true,
'Stmt_If' => true,
'Stmt_Switch' => true,
'Stmt_While' => true,
'Stmt_TryCatch' => true,
'Stmt_Label' => true,
);
protected $precedenceStack = array(19);
protected $precedenceStackPos = 0;
protected $precedenceStack;
protected $precedenceStackPos;
protected $noIndentToken;
/**
* Pretty prints an array of statements.
* Pretty prints an array of nodes (statements).
*
* @param array $nodes Array of statements
* @param array $nodes Array of nodes
*
* @return string Pretty printed nodes
*/
public function prettyPrint(array $nodes) {
$this->precedenceStack = array($this->precedenceStackPos = 0 => 19);
$this->noIndentToken = uniqid('_NO_INDENT_');
return str_replace("\n" . $this->noIndentToken, "\n", $this->pStmts($nodes, false));
}
/**
* Pretty prints an array of nodes (statements) and indents them optionally.
*
* @param array $nodes Array of nodes
* @param bool $indent Whether to indent the printed nodes
*
* @return string Pretty printed statements
*/
public function pStmts(array $nodes) {
$return = '';
protected function pStmts(array $nodes, $indent = true) {
$pNodes = array();
foreach ($nodes as $node) {
$return .= $this->p($node);
if ( $node instanceof Node_Stmt_Func
|| $node instanceof Node_Stmt_Interface
|| $node instanceof Node_Stmt_Class
|| $node instanceof Node_Stmt_ClassMethod
|| $node instanceof Node_Stmt_For
|| $node instanceof Node_Stmt_Foreach
|| $node instanceof Node_Stmt_If
|| $node instanceof Node_Stmt_Switch
|| $node instanceof Node_Stmt_While
|| $node instanceof Node_Stmt_TryCatch
|| $node instanceof Node_Stmt_Label
) {
$return .= "\n";
} else {
$return .= ';' . "\n";
}
$pNodes[] = $this->p($node)
. (isset($this->stmtsWithoutSemicolon[$node->getType()]) ? '' : ';');
}
if ($indent) {
return ' ' . preg_replace(
'~\n(?!$|' . $this->noIndentToken . ')~',
"\n" . ' ',
implode("\n", $pNodes)
);
} else {
return implode("\n", $pNodes);
}
return $return;
}
/**
@ -99,7 +121,7 @@ abstract class PrettyPrinterAbstract
*
* @return string Pretty printed node
*/
public function p(NodeAbstract $node) {
protected function p(NodeAbstract $node) {
$type = $node->getType();
if (isset($this->precedanceMap[$type])) {
@ -150,20 +172,13 @@ abstract class PrettyPrinterAbstract
}
/**
* Indents a string by four space characters.
* Signifies the pretty printer that a string shall not be indented.
*
* @param string $string String to indent
* @param string $string Not to be indented string
*
* @return string Indented string
* @return mixed String marked with $this->noIndentToken's.
*/
protected function pIndent($string) {
$lines = explode("\n", $string);
foreach ($lines as &$line) {
if ('' !== $line) {
$line = ' ' . $line;
}
}
return implode("\n", $lines);
protected function pSafe($string) {
return str_replace("\n", "\n" . $this->noIndentToken, $string);
}
}

View File

@ -54,7 +54,7 @@ foreach (new RecursiveIteratorIterator(
if (false !== $stmts) {
++$ppCount;
$ppTime -= microtime(true);
$code = '<?php' . "\n" . $prettyPrinter->pStmts($stmts);
$code = '<?php' . "\n" . $prettyPrinter->prettyPrint($stmts);
$ppTime += microtime(true);
$ppStmts = $parser->parse(