mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-30 04:29:15 +01:00
Add string kinds and doc string labels
Scalar\String_ and Scalar\Encapsed now have an additional "kind" attribute, which may be one of: * String_::KIND_SINGLE_QUOTED * String_::KIND_DOUBLE_QUOTED * String_::KIND_NOWDOC * String_::KIND_HEREDOC Additionally, if the string kind is one of the latter two, an attribute "docLabel" is provided, which contains the doc string label (STR in <<<STR) that was originally used. The pretty printer will try to take the original kind of the string, as well as the used doc string label into account.
This commit is contained in:
parent
fa6a17755a
commit
588e6a4d4c
@ -628,7 +628,8 @@ array_expr:
|
||||
scalar_dereference:
|
||||
array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']'
|
||||
{ $$ = Expr\ArrayDimFetch[Scalar\String_[Scalar\String_::parse($1, false)], $3]; }
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; }
|
||||
| constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
| scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; }
|
||||
/* alternative array syntax missing intentionally */
|
||||
@ -741,7 +742,9 @@ ctor_arguments:
|
||||
common_scalar:
|
||||
T_LNUMBER { $$ = Scalar\LNumber::fromString($1, attributes()); }
|
||||
| T_DNUMBER { $$ = Scalar\DNumber[Scalar\DNumber::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1, false)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); }
|
||||
| T_LINE { $$ = Scalar\MagicConst\Line[]; }
|
||||
| T_FILE { $$ = Scalar\MagicConst\File[]; }
|
||||
| T_DIR { $$ = Scalar\MagicConst\Dir[]; }
|
||||
@ -751,9 +754,11 @@ common_scalar:
|
||||
| T_FUNC_C { $$ = Scalar\MagicConst\Function_[]; }
|
||||
| T_NS_C { $$ = Scalar\MagicConst\Namespace_[]; }
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2, false)]; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parseDocString($1, $2, false), $attrs); }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = Scalar\String_['']; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_('', $attrs); }
|
||||
;
|
||||
|
||||
static_scalar:
|
||||
@ -811,9 +816,11 @@ scalar:
|
||||
common_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| '"' encaps_list '"'
|
||||
{ parseEncapsed($2, '"', false); $$ = Scalar\Encapsed[$2]; }
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ parseEncapsedDoc($2, false); $$ = Scalar\Encapsed[$2]; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
parseEncapsedDoc($2, true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
;
|
||||
|
||||
static_array_pair_list:
|
||||
|
@ -679,7 +679,9 @@ dereferencable_scalar:
|
||||
| '[' array_pair_list ']'
|
||||
{ $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_SHORT;
|
||||
$$ = new Expr\Array_($2, $attrs); }
|
||||
| T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_[Scalar\String_::parse($1)]; }
|
||||
| T_CONSTANT_ENCAPSED_STRING
|
||||
{ $attrs = attributes(); $attrs['kind'] = strKind($1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); }
|
||||
;
|
||||
|
||||
scalar:
|
||||
@ -696,13 +698,17 @@ scalar:
|
||||
| dereferencable_scalar { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| T_START_HEREDOC T_ENCAPSED_AND_WHITESPACE T_END_HEREDOC
|
||||
{ $$ = Scalar\String_[Scalar\String_::parseDocString($1, $2)]; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_(Scalar\String_::parseDocString($1, $2), $attrs); }
|
||||
| T_START_HEREDOC T_END_HEREDOC
|
||||
{ $$ = Scalar\String_['']; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
$$ = new Scalar\String_('', $attrs); }
|
||||
| '"' encaps_list '"'
|
||||
{ parseEncapsed($2, '"', true); $$ = Scalar\Encapsed[$2]; }
|
||||
{ $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
| T_START_HEREDOC encaps_list T_END_HEREDOC
|
||||
{ parseEncapsedDoc($2, true); $$ = Scalar\Encapsed[$2]; }
|
||||
{ $attrs = attributes(); setDocStringAttrs($attrs, $1);
|
||||
parseEncapsedDoc($2, true); $$ = new Scalar\Encapsed($2, $attrs); }
|
||||
;
|
||||
|
||||
optional_comma:
|
||||
|
@ -178,6 +178,23 @@ function resolveMacros($code) {
|
||||
. ' else { ' . $args[0] . ' = null; }';
|
||||
}
|
||||
|
||||
if ('strKind' == $name) {
|
||||
assertArgs(1, $args, $name);
|
||||
|
||||
return '(' . $args[0] . '[0] === "\'" || (' . $args[0] . '[1] === "\'" && '
|
||||
. '(' . $args[0] . '[0] === \'b\' || ' . $args[0] . '[0] === \'B\')) '
|
||||
. '? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED)';
|
||||
}
|
||||
|
||||
if ('setDocStringAttrs' == $name) {
|
||||
assertArgs(2, $args, $name);
|
||||
|
||||
return $args[0] . '[\'kind\'] = strpos(' . $args[1] . ', "\'") === false '
|
||||
. '? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; '
|
||||
. 'preg_match(\'/\A[bB]?<<<[ \t]*[\\\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\\\'"]?(?:\r\n|\n|\r)\z/\', ' . $args[1] . ', $matches); '
|
||||
. $args[0] . '[\'docLabel\'] = $matches[1];';
|
||||
}
|
||||
|
||||
return $matches[0];
|
||||
},
|
||||
$code
|
||||
|
@ -7,6 +7,12 @@ use PhpParser\Node\Scalar;
|
||||
|
||||
class String_ extends Scalar
|
||||
{
|
||||
/* For use in "kind" attribute */
|
||||
const KIND_SINGLE_QUOTED = 1;
|
||||
const KIND_DOUBLE_QUOTED = 2;
|
||||
const KIND_HEREDOC = 3;
|
||||
const KIND_NOWDOC = 4;
|
||||
|
||||
/** @var string String value */
|
||||
public $value;
|
||||
|
||||
|
@ -2429,7 +2429,8 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule379() {
|
||||
$this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(4-1)], false), $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes), $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$this->stackPos-(4-1)][0] === "'" || ($this->semStack[$this->stackPos-(4-1)][1] === "'" && ($this->semStack[$this->stackPos-(4-1)][0] === 'b' || $this->semStack[$this->stackPos-(4-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED);
|
||||
$this->semValue = new Expr\ArrayDimFetch(new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(4-1)]), $attrs), $this->semStack[$this->stackPos-(4-3)], $this->startAttributeStack[$this->stackPos-(4-1)] + $this->endAttributes);
|
||||
}
|
||||
|
||||
protected function reduceRule380() {
|
||||
@ -2611,7 +2612,8 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule421() {
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)], false), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$this->stackPos-(1-1)][0] === "'" || ($this->semStack[$this->stackPos-(1-1)][1] === "'" && ($this->semStack[$this->stackPos-(1-1)][0] === 'b' || $this->semStack[$this->stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED);
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)], false), $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule422() {
|
||||
@ -2647,11 +2649,13 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule430() {
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)], false), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(3-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(3-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)], false), $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule431() {
|
||||
$this->semValue = new Scalar\String_('', $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(2-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(2-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
$this->semValue = new Scalar\String_('', $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule432() {
|
||||
@ -2827,11 +2831,13 @@ class Php5 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule475() {
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', false); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule476() {
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, false); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(3-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(3-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, true); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule477() {
|
||||
|
@ -2375,7 +2375,8 @@ class Php7 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule395() {
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)]), $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$this->stackPos-(1-1)][0] === "'" || ($this->semStack[$this->stackPos-(1-1)][1] === "'" && ($this->semStack[$this->stackPos-(1-1)][0] === 'b' || $this->semStack[$this->stackPos-(1-1)][0] === 'B')) ? Scalar\String_::KIND_SINGLE_QUOTED : Scalar\String_::KIND_DOUBLE_QUOTED);
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parse($this->semStack[$this->stackPos-(1-1)]), $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule396() {
|
||||
@ -2427,19 +2428,23 @@ class Php7 extends \PhpParser\ParserAbstract
|
||||
}
|
||||
|
||||
protected function reduceRule408() {
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)]), $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(3-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(3-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
$this->semValue = new Scalar\String_(Scalar\String_::parseDocString($this->semStack[$this->stackPos-(3-1)], $this->semStack[$this->stackPos-(3-2)]), $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule409() {
|
||||
$this->semValue = new Scalar\String_('', $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(2-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(2-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(2-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
$this->semValue = new Scalar\String_('', $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule410() {
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED;
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '"', true); } }; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule411() {
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, true); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes);
|
||||
$attrs = $this->startAttributeStack[$this->stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = strpos($this->semStack[$this->stackPos-(3-1)], "'") === false ? Scalar\String_::KIND_HEREDOC : Scalar\String_::KIND_NOWDOC; preg_match('/\A[bB]?<<<[ \t]*[\'"]?([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)[\'"]?(?:\r\n|\n|\r)\z/', $this->semStack[$this->stackPos-(3-1)], $matches); $attrs['docLabel'] = $matches[1];;
|
||||
foreach ($this->semStack[$this->stackPos-(3-2)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, null, true); } } $s->value = preg_replace('~(\r\n|\n|\r)\z~', '', $s->value); if ('' === $s->value) array_pop($this->semStack[$this->stackPos-(3-2)]);; $this->semValue = new Scalar\Encapsed($this->semStack[$this->stackPos-(3-2)], $attrs);
|
||||
}
|
||||
|
||||
protected function reduceRule412() {
|
||||
|
@ -84,10 +84,55 @@ class Standard extends PrettyPrinterAbstract
|
||||
// Scalars
|
||||
|
||||
public function pScalar_String(Scalar\String_ $node) {
|
||||
return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
|
||||
$kind = $node->getAttribute('kind', Scalar\String_::KIND_SINGLE_QUOTED);
|
||||
switch ($kind) {
|
||||
case Scalar\String_::KIND_NOWDOC:
|
||||
$label = $node->getAttribute('docLabel');
|
||||
if ($label && !$this->containsEndLabel($node->value, $label)) {
|
||||
if ($node->value === '') {
|
||||
return $this->pNoIndent("<<<'$label'\n$label") . $this->docStringEndToken;
|
||||
}
|
||||
|
||||
return $this->pNoIndent("<<<'$label'\n$node->value\n$label")
|
||||
. $this->docStringEndToken;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case Scalar\String_::KIND_SINGLE_QUOTED:
|
||||
return '\'' . $this->pNoIndent(addcslashes($node->value, '\'\\')) . '\'';
|
||||
case Scalar\String_::KIND_HEREDOC:
|
||||
$label = $node->getAttribute('docLabel');
|
||||
if ($label && !$this->containsEndLabel($node->value, $label)) {
|
||||
if ($node->value === '') {
|
||||
return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
|
||||
}
|
||||
|
||||
$escaped = $this->escapeString($node->value, null);
|
||||
return $this->pNoIndent("<<<$label\n" . $escaped ."\n$label")
|
||||
. $this->docStringEndToken;
|
||||
}
|
||||
/* break missing intentionally */
|
||||
case Scalar\String_::KIND_DOUBLE_QUOTED:
|
||||
return '"' . $this->escapeString($node->value, '"') . '"';
|
||||
}
|
||||
throw new \Exception('Invalid string kind');
|
||||
}
|
||||
|
||||
public function pScalar_Encapsed(Scalar\Encapsed $node) {
|
||||
if ($node->getAttribute('kind') === Scalar\String_::KIND_HEREDOC) {
|
||||
$label = $node->getAttribute('docLabel');
|
||||
if ($label && !$this->encapsedContainsEndLabel($node->parts, $label)) {
|
||||
if (count($node->parts) === 1
|
||||
&& $node->parts[0] instanceof Scalar\EncapsedStringPart
|
||||
&& $node->parts[0]->value === ''
|
||||
) {
|
||||
return $this->pNoIndent("<<<$label\n$label") . $this->docStringEndToken;
|
||||
}
|
||||
|
||||
return $this->pNoIndent(
|
||||
"<<<$label\n" . $this->pEncapsList($node->parts, null) . "\n$label"
|
||||
) . $this->docStringEndToken;
|
||||
}
|
||||
}
|
||||
return '"' . $this->pEncapsList($node->parts, '"') . '"';
|
||||
}
|
||||
|
||||
@ -103,6 +148,7 @@ class Standard extends PrettyPrinterAbstract
|
||||
case Scalar\LNumber::KIND_HEX:
|
||||
return '0x' . base_convert($str, 10, 16);
|
||||
}
|
||||
throw new \Exception('Invalid number kind');
|
||||
}
|
||||
|
||||
public function pScalar_DNumber(Scalar\DNumber $node) {
|
||||
@ -790,7 +836,7 @@ class Standard extends PrettyPrinterAbstract
|
||||
$return = '';
|
||||
foreach ($encapsList as $element) {
|
||||
if ($element instanceof Scalar\EncapsedStringPart) {
|
||||
$return .= addcslashes($element->value, "\n\r\t\f\v$" . $quote . "\\");
|
||||
$return .= $this->escapeString($element->value, $quote);
|
||||
} else {
|
||||
$return .= '{' . $this->p($element) . '}';
|
||||
}
|
||||
@ -799,6 +845,34 @@ class Standard extends PrettyPrinterAbstract
|
||||
return $return;
|
||||
}
|
||||
|
||||
protected function escapeString($string, $quote) {
|
||||
if (null === $quote) {
|
||||
// For doc strings, don't escape newlines
|
||||
return addcslashes($string, "\t\f\v$\\");
|
||||
}
|
||||
return addcslashes($string, "\n\r\t\f\v$" . $quote . "\\");
|
||||
}
|
||||
|
||||
protected function containsEndLabel($string, $label, $atStart = true, $atEnd = true) {
|
||||
$start = $atStart ? '(?:^|[\r\n])' : '[\r\n]';
|
||||
$end = $atEnd ? '(?:$|[;\r\n])' : '[;\r\n]';
|
||||
return false !== strpos($string, $label)
|
||||
&& preg_match('/' . $start . $label . $end . '/', $string);
|
||||
}
|
||||
|
||||
protected function encapsedContainsEndLabel(array $parts, $label) {
|
||||
foreach ($parts as $i => $part) {
|
||||
$atStart = $i === 0;
|
||||
$atEnd = $i === count($parts) - 1;
|
||||
if ($part instanceof Scalar\EncapsedStringPart
|
||||
&& $this->containsEndLabel($part->value, $label, $atStart, $atEnd)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function pDereferenceLhs(Node $node) {
|
||||
if ($node instanceof Expr\Variable
|
||||
|| $node instanceof Name
|
||||
|
@ -75,6 +75,7 @@ abstract class PrettyPrinterAbstract
|
||||
);
|
||||
|
||||
protected $noIndentToken;
|
||||
protected $docStringEndToken;
|
||||
protected $canUseSemicolonNamespaces;
|
||||
protected $options;
|
||||
|
||||
@ -89,6 +90,7 @@ abstract class PrettyPrinterAbstract
|
||||
*/
|
||||
public function __construct(array $options = []) {
|
||||
$this->noIndentToken = '_NO_INDENT_' . mt_rand();
|
||||
$this->docStringEndToken = '_DOC_STRING_END_' . mt_rand();
|
||||
|
||||
$defaultOptions = ['shortArraySyntax' => false];
|
||||
$this->options = $options + $defaultOptions;
|
||||
@ -104,7 +106,7 @@ abstract class PrettyPrinterAbstract
|
||||
public function prettyPrint(array $stmts) {
|
||||
$this->preprocessNodes($stmts);
|
||||
|
||||
return ltrim(str_replace("\n" . $this->noIndentToken, "\n", $this->pStmts($stmts, false)));
|
||||
return ltrim($this->handleMagicTokens($this->pStmts($stmts, false)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +117,7 @@ abstract class PrettyPrinterAbstract
|
||||
* @return string Pretty printed node
|
||||
*/
|
||||
public function prettyPrintExpr(Expr $node) {
|
||||
return str_replace("\n" . $this->noIndentToken, "\n", $this->p($node));
|
||||
return $this->handleMagicTokens($this->p($node));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,6 +159,17 @@ abstract class PrettyPrinterAbstract
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleMagicTokens($str) {
|
||||
// Drop no-indent tokens
|
||||
$str = str_replace($this->noIndentToken, '', $str);
|
||||
|
||||
// Replace doc-string-end tokens with nothing or a newline
|
||||
$str = str_replace($this->docStringEndToken . ";\n", ";\n", $str);
|
||||
$str = str_replace($this->docStringEndToken, "\n", $str);
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty prints an array of nodes (statements) and indents them optionally.
|
||||
*
|
||||
|
@ -3,6 +3,9 @@
|
||||
namespace PhpParser;
|
||||
|
||||
use PhpParser\Comment;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Scalar;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
|
||||
abstract class ParserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
@ -105,6 +108,52 @@ EOC;
|
||||
$parser = $this->getParser($lexer);
|
||||
$parser->parse('dummy');
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestKindAttributes
|
||||
*/
|
||||
public function testKindAttributes($code, $expectedAttributes) {
|
||||
$parser = $this->getParser(new Lexer);
|
||||
$stmts = $parser->parse("<?php $code;");
|
||||
$attributes = $stmts[0]->getAttributes();
|
||||
foreach ($expectedAttributes as $name => $value) {
|
||||
$this->assertSame($value, $attributes[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
public function provideTestKindAttributes() {
|
||||
return array(
|
||||
array('0', ['kind' => Scalar\LNumber::KIND_DEC]),
|
||||
array('9', ['kind' => Scalar\LNumber::KIND_DEC]),
|
||||
array('07', ['kind' => Scalar\LNumber::KIND_OCT]),
|
||||
array('0xf', ['kind' => Scalar\LNumber::KIND_HEX]),
|
||||
array('0XF', ['kind' => Scalar\LNumber::KIND_HEX]),
|
||||
array('0b1', ['kind' => Scalar\LNumber::KIND_BIN]),
|
||||
array('0B1', ['kind' => Scalar\LNumber::KIND_BIN]),
|
||||
array('[]', ['kind' => Expr\Array_::KIND_SHORT]),
|
||||
array('array()', ['kind' => Expr\Array_::KIND_LONG]),
|
||||
array("'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
|
||||
array("b'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
|
||||
array("B'foo'", ['kind' => String_::KIND_SINGLE_QUOTED]),
|
||||
array('"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array('b"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array('B"foo"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array('"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array('b"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array('B"foo$bar"', ['kind' => String_::KIND_DOUBLE_QUOTED]),
|
||||
array("<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
|
||||
array("<<<STR\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
array("<<<\"STR\"\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
array("b<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
|
||||
array("B<<<'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
|
||||
array("<<< \t 'STR'\nSTR\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']),
|
||||
array("<<<'\xff'\n\xff\n", ['kind' => String_::KIND_NOWDOC, 'docLabel' => "\xff"]),
|
||||
array("<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
array("b<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
array("B<<<\"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
array("<<< \t \"STR\"\n\$a\nSTR\n", ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR']),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidTokenLexer extends Lexer {
|
||||
|
@ -4,6 +4,8 @@ namespace PhpParser;
|
||||
|
||||
use PhpParser\Comment;
|
||||
use PhpParser\Node\Expr;
|
||||
use PhpParser\Node\Scalar\Encapsed;
|
||||
use PhpParser\Node\Scalar\EncapsedStringPart;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt;
|
||||
use PhpParser\PrettyPrinter\Standard;
|
||||
@ -111,4 +113,52 @@ class PrettyPrinterTest extends CodeTestAbstract
|
||||
$expected = "['key' => 'val']";
|
||||
$this->assertSame($expected, $prettyPrinter->prettyPrintExpr($expr));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideTestKindAttributes
|
||||
*/
|
||||
public function testKindAttributes($node, $expected) {
|
||||
$prttyPrinter = new PrettyPrinter\Standard;
|
||||
$result = $prttyPrinter->prettyPrintExpr($node);
|
||||
$this->assertSame($expected, $result);
|
||||
}
|
||||
|
||||
public function provideTestKindAttributes() {
|
||||
$nowdoc = ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR'];
|
||||
$heredoc = ['kind' => String_::KIND_HEREDOC, 'docLabel' => 'STR'];
|
||||
return [
|
||||
// Defaults to single quoted
|
||||
[new String_('foo'), "'foo'"],
|
||||
// Explicit single/double quoted
|
||||
[new String_('foo', ['kind' => String_::KIND_SINGLE_QUOTED]), "'foo'"],
|
||||
[new String_('foo', ['kind' => String_::KIND_DOUBLE_QUOTED]), '"foo"'],
|
||||
// Fallback from doc string if no label
|
||||
[new String_('foo', ['kind' => String_::KIND_NOWDOC]), "'foo'"],
|
||||
[new String_('foo', ['kind' => String_::KIND_HEREDOC]), '"foo"'],
|
||||
// Fallback if string contains label
|
||||
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'A']), "'A\nB\nC'"],
|
||||
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'B']), "'A\nB\nC'"],
|
||||
[new String_("A\nB\nC", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'C']), "'A\nB\nC'"],
|
||||
[new String_("STR;", ['kind' => String_::KIND_NOWDOC, 'docLabel' => 'STR']), "'STR;'"],
|
||||
// Doc string if label not contained (or not in ending position)
|
||||
[new String_("foo", $nowdoc), "<<<'STR'\nfoo\nSTR\n"],
|
||||
[new String_("foo", $heredoc), "<<<STR\nfoo\nSTR\n"],
|
||||
[new String_("STRx", $nowdoc), "<<<'STR'\nSTRx\nSTR\n"],
|
||||
[new String_("xSTR", $nowdoc), "<<<'STR'\nxSTR\nSTR\n"],
|
||||
// Empty doc string variations (encapsed variant does not occur naturally)
|
||||
[new String_("", $nowdoc), "<<<'STR'\nSTR\n"],
|
||||
[new String_("", $heredoc), "<<<STR\nSTR\n"],
|
||||
[new Encapsed([new EncapsedStringPart('')], $heredoc), "<<<STR\nSTR\n"],
|
||||
// Encapsed doc string variations
|
||||
[new Encapsed([new EncapsedStringPart('foo')], $heredoc), "<<<STR\nfoo\nSTR\n"],
|
||||
[new Encapsed([new EncapsedStringPart('foo'), new Expr\Variable('y')], $heredoc), "<<<STR\nfoo{\$y}\nSTR\n"],
|
||||
[new Encapsed([new EncapsedStringPart("\nSTR"), new Expr\Variable('y')], $heredoc), "<<<STR\n\nSTR{\$y}\nSTR\n"],
|
||||
[new Encapsed([new EncapsedStringPart("\nSTR"), new Expr\Variable('y')], $heredoc), "<<<STR\n\nSTR{\$y}\nSTR\n"],
|
||||
[new Encapsed([new Expr\Variable('y'), new EncapsedStringPart("STR\n")], $heredoc), "<<<STR\n{\$y}STR\n\nSTR\n"],
|
||||
// Encapsed doc string fallback
|
||||
[new Encapsed([new Expr\Variable('y'), new EncapsedStringPart("\nSTR")], $heredoc), '"{$y}\\nSTR"'],
|
||||
[new Encapsed([new EncapsedStringPart("STR\n"), new Expr\Variable('y')], $heredoc), '"STR\\n{$y}"'],
|
||||
[new Encapsed([new EncapsedStringPart("STR")], $heredoc), '"STR"'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +136,9 @@ CODE;
|
||||
<attribute:endLine>
|
||||
<scalar:int>5</scalar:int>
|
||||
</attribute:endLine>
|
||||
<attribute:kind>
|
||||
<scalar:int>1</scalar:int>
|
||||
</attribute:kind>
|
||||
<subNode:value>
|
||||
<scalar:string>Foo</scalar:string>
|
||||
</subNode:value>
|
||||
|
86
test/code/prettyPrinter/expr/docStrings.test
Normal file
86
test/code/prettyPrinter/expr/docStrings.test
Normal file
@ -0,0 +1,86 @@
|
||||
Literals
|
||||
-----
|
||||
<?php
|
||||
|
||||
<<<'STR'
|
||||
STR;
|
||||
<<<STR
|
||||
STR;
|
||||
|
||||
<<<'STR'
|
||||
A
|
||||
B
|
||||
STR;
|
||||
<<<STR
|
||||
A
|
||||
B
|
||||
STR;
|
||||
|
||||
<<<'STR'
|
||||
a\nb$c
|
||||
STR;
|
||||
<<<STR
|
||||
a\\nb\$c
|
||||
STR;
|
||||
|
||||
<<<STR
|
||||
a$b
|
||||
{$c->d}
|
||||
STR;
|
||||
|
||||
call(
|
||||
<<<STR
|
||||
A
|
||||
STR
|
||||
, <<<STR
|
||||
B
|
||||
STR
|
||||
);
|
||||
|
||||
function test() {
|
||||
<<<STR
|
||||
Foo
|
||||
STR;
|
||||
<<<STR
|
||||
Bar
|
||||
STR;
|
||||
}
|
||||
-----
|
||||
<<<'STR'
|
||||
STR;
|
||||
<<<STR
|
||||
STR;
|
||||
<<<'STR'
|
||||
A
|
||||
B
|
||||
STR;
|
||||
<<<STR
|
||||
A
|
||||
B
|
||||
STR;
|
||||
<<<'STR'
|
||||
a\nb$c
|
||||
STR;
|
||||
<<<STR
|
||||
a\\nb\$c
|
||||
STR;
|
||||
<<<STR
|
||||
a{$b}
|
||||
{$c->d}
|
||||
STR;
|
||||
call(<<<STR
|
||||
A
|
||||
STR
|
||||
, <<<STR
|
||||
B
|
||||
STR
|
||||
);
|
||||
function test()
|
||||
{
|
||||
<<<STR
|
||||
Foo
|
||||
STR;
|
||||
<<<STR
|
||||
Bar
|
||||
STR;
|
||||
}
|
@ -11,7 +11,7 @@ exit();
|
||||
exit(1);
|
||||
die;
|
||||
die();
|
||||
die("foo");
|
||||
die('foo');
|
||||
clone $foo;
|
||||
eval('str');
|
||||
-----
|
||||
|
@ -40,21 +40,17 @@ FALSE;
|
||||
378282246310005.0;
|
||||
10000000000000002.0;
|
||||
|
||||
// strings (normalized to single quoted)
|
||||
// strings (single quoted)
|
||||
'a';
|
||||
'a
|
||||
b';
|
||||
"a";
|
||||
"a\nb";
|
||||
'a\'b';
|
||||
|
||||
// strings (double quoted)
|
||||
"a'b";
|
||||
"a\b";
|
||||
<<<'STR'
|
||||
a\nb$a
|
||||
{$b}
|
||||
STR;
|
||||
|
||||
// strings (normalized to double quoted)
|
||||
"$a";
|
||||
"a$b";
|
||||
"$a$b";
|
||||
@ -68,9 +64,6 @@ STR;
|
||||
"\\{ $A }";
|
||||
"{$$A}[B]";
|
||||
"$$A[B]";
|
||||
<<<STR
|
||||
a\nb$a\n{$b}
|
||||
STR;
|
||||
|
||||
// make sure indentation doesn't mess anything up
|
||||
function foo()
|
||||
@ -122,19 +115,16 @@ INF;
|
||||
1.0E+84;
|
||||
378282246310005.0;
|
||||
10000000000000002.0;
|
||||
// strings (normalized to single quoted)
|
||||
'a';
|
||||
'a
|
||||
b';
|
||||
// strings (single quoted)
|
||||
'a';
|
||||
'a
|
||||
b';
|
||||
"a";
|
||||
"a\nb";
|
||||
'a\'b';
|
||||
'a\'b';
|
||||
'a\\b';
|
||||
'a\\nb$a
|
||||
{$b}';
|
||||
// strings (normalized to double quoted)
|
||||
// strings (double quoted)
|
||||
"a'b";
|
||||
"a\\b";
|
||||
"{$a}";
|
||||
"a{$b}";
|
||||
"{$a}{$b}";
|
||||
@ -148,12 +138,10 @@ b';
|
||||
"\\{ {$A} }";
|
||||
"{${$A}}[B]";
|
||||
"\${$A['B']}";
|
||||
"a\nb{$a}\n{$b}";
|
||||
// make sure indentation doesn't mess anything up
|
||||
function foo()
|
||||
{
|
||||
'a
|
||||
b';
|
||||
"a\nb";
|
||||
'a
|
||||
b';
|
||||
'a
|
||||
|
Loading…
Reference in New Issue
Block a user