diff --git a/grammar/php5.y b/grammar/php5.y index a62e9a310c..a95e840659 100644 --- a/grammar/php5.y +++ b/grammar/php5.y @@ -779,8 +779,7 @@ exit_expr: backticks_expr: /* empty */ { $$ = array(); } - | T_ENCAPSED_AND_WHITESPACE - { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`', false)]); } + | T_ENCAPSED_AND_WHITESPACE { $$ = array(Scalar\EncapsedStringPart::fromStringParsed($1, attributes(), '`', false)); } | encaps_list { parseEncapsed($1, '`', false); $$ = $1; } ; @@ -1009,7 +1008,7 @@ encaps_list: ; encaps_string_part: - T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; } + T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart::fromString($1, attributes()); } ; encaps_str_varname: diff --git a/grammar/php7.y b/grammar/php7.y index 087bc7392e..c33fd53857 100644 --- a/grammar/php7.y +++ b/grammar/php7.y @@ -982,8 +982,7 @@ exit_expr: backticks_expr: /* empty */ { $$ = array(); } - | T_ENCAPSED_AND_WHITESPACE - { $$ = array(Scalar\EncapsedStringPart[Scalar\String_::parseEscapeSequences($1, '`')]); } + | T_ENCAPSED_AND_WHITESPACE { $$ = array(Scalar\EncapsedStringPart::fromStringParsed($1, attributes(), '`')); } | encaps_list { parseEncapsed($1, '`', true); $$ = $1; } ; @@ -1173,7 +1172,7 @@ encaps_list: ; encaps_string_part: - T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart[$1]; } + T_ENCAPSED_AND_WHITESPACE { $$ = Scalar\EncapsedStringPart::fromString($1, attributes()); } ; encaps_str_varname: diff --git a/lib/PhpParser/Node/Scalar/EncapsedStringPart.php b/lib/PhpParser/Node/Scalar/EncapsedStringPart.php index bb3194c1d7..b8a99e8a45 100644 --- a/lib/PhpParser/Node/Scalar/EncapsedStringPart.php +++ b/lib/PhpParser/Node/Scalar/EncapsedStringPart.php @@ -20,10 +20,29 @@ public function __construct(string $value, array $attributes = []) { $this->value = $value; } + public static function fromString(string $value, array $attributes): self + { + $attributes['rawValue'] = $value; + + return new self($value, $attributes); + } + + /** + * @param null|string $quote Quote type + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + */ + public static function fromStringParsed(string $value, array $attributes, $quote, bool $parseUnicodeEscape = true): self + { + $attributes['rawValue'] = $value; + $parsedValue = String_::parseEscapeSequences($value, $quote, $parseUnicodeEscape); + + return new self($parsedValue, $attributes); + } + public function getSubNodeNames() : array { return ['value']; } - + public function getType() : string { return 'Scalar_EncapsedStringPart'; } diff --git a/lib/PhpParser/Parser/Php5.php b/lib/PhpParser/Parser/Php5.php index d9c8fe0494..1ed5ae5fbb 100644 --- a/lib/PhpParser/Parser/Php5.php +++ b/lib/PhpParser/Parser/Php5.php @@ -2259,7 +2259,7 @@ protected function initReduceCallbacks() { $this->semValue = array(); }, 429 => function ($stackPos) { - $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`', false), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + $this->semValue = array(Scalar\EncapsedStringPart::fromStringParsed($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, '`', false)); }, 430 => function ($stackPos) { foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', false); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; @@ -2632,7 +2632,7 @@ protected function initReduceCallbacks() { $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); }, 553 => function ($stackPos) { - $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = Scalar\EncapsedStringPart::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 554 => function ($stackPos) { $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); diff --git a/lib/PhpParser/Parser/Php7.php b/lib/PhpParser/Parser/Php7.php index 71ba0187ee..64405a0e09 100644 --- a/lib/PhpParser/Parser/Php7.php +++ b/lib/PhpParser/Parser/Php7.php @@ -2513,7 +2513,7 @@ protected function initReduceCallbacks() { $this->semValue = array(); }, 498 => function ($stackPos) { - $this->semValue = array(new Scalar\EncapsedStringPart(Scalar\String_::parseEscapeSequences($this->semStack[$stackPos-(1-1)], '`'), $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes)); + $this->semValue = array(Scalar\EncapsedStringPart::fromStringParsed($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, '`')); }, 499 => function ($stackPos) { foreach ($this->semStack[$stackPos-(1-1)] as $s) { if ($s instanceof Node\Scalar\EncapsedStringPart) { $s->value = Node\Scalar\String_::parseEscapeSequences($s->value, '`', true); } }; $this->semValue = $this->semStack[$stackPos-(1-1)]; @@ -2783,7 +2783,7 @@ protected function initReduceCallbacks() { $this->semValue = array($this->semStack[$stackPos-(2-1)], $this->semStack[$stackPos-(2-2)]); }, 587 => function ($stackPos) { - $this->semValue = new Scalar\EncapsedStringPart($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); + $this->semValue = Scalar\EncapsedStringPart::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 588 => function ($stackPos) { $this->semValue = new Expr\Variable($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); diff --git a/test/PhpParser/Node/Scalar/EncapsedStringPartTest.php b/test/PhpParser/Node/Scalar/EncapsedStringPartTest.php new file mode 100644 index 0000000000..400d59870b --- /dev/null +++ b/test/PhpParser/Node/Scalar/EncapsedStringPartTest.php @@ -0,0 +1,37 @@ +create(ParserFactory::PREFER_PHP7); + $nodes = $parser->parse('assertInstanceOf(Echo_::class, $echo); + + /** @var Echo_ $echoExprs */ + $echoExprs = $echo->exprs; + + $this->assertCount(1, $echoExprs); + + $firstEchoExprs = $echoExprs[0]; + $this->assertInstanceOf(Encapsed::class, $firstEchoExprs); + + /** @var Encapsed $firstEchoExprs */ + $this->assertCount(2, $firstEchoExprs->parts); + + $secondEncapsedPart = $firstEchoExprs->parts[1]; + $this->assertInstanceOf(EncapsedStringPart::class, $secondEncapsedPart); + + $this->assertSame( ' some "', $secondEncapsedPart->value); + $this->assertSame(' some \\"', $secondEncapsedPart->getAttribute('rawValue')); + } +}