diff --git a/grammar/php5.y b/grammar/php5.y index 927762b450..a62e9a310c 100644 --- a/grammar/php5.y +++ b/grammar/php5.y @@ -689,9 +689,7 @@ array_expr: scalar_dereference: array_expr '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } - | T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' - { $attrs = attributes(); $attrs['kind'] = strKind($1); - $$ = Expr\ArrayDimFetch[new Scalar\String_(Scalar\String_::parse($1), $attrs), $3]; } + | T_CONSTANT_ENCAPSED_STRING '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[Scalar\String_::fromString($1, attributes()), $3]; } | constant '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } | scalar_dereference '[' dim_offset ']' { $$ = Expr\ArrayDimFetch[$1, $3]; } /* alternative array syntax missing intentionally */ @@ -794,9 +792,7 @@ ctor_arguments: common_scalar: T_LNUMBER { $$ = $this->parseLNumber($1, attributes(), true); } | T_DNUMBER { $$ = Scalar\DNumber::fromString($1, attributes()); } - | T_CONSTANT_ENCAPSED_STRING - { $attrs = attributes(); $attrs['kind'] = strKind($1); - $$ = new Scalar\String_(Scalar\String_::parse($1, false), $attrs); } + | T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes(), false); } | T_LINE { $$ = Scalar\MagicConst\Line[]; } | T_FILE { $$ = Scalar\MagicConst\File[]; } | T_DIR { $$ = Scalar\MagicConst\Dir[]; } diff --git a/grammar/php7.y b/grammar/php7.y index 379581f6a0..665ad5efa7 100644 --- a/grammar/php7.y +++ b/grammar/php7.y @@ -1014,9 +1014,7 @@ dereferencable_scalar: { $attrs = attributes(); $attrs['kind'] = Expr\Array_::KIND_LONG; $$ = new Expr\Array_($3, $attrs); } | array_short_syntax { $$ = $1; } - | T_CONSTANT_ENCAPSED_STRING - { $attrs = attributes(); $attrs['kind'] = strKind($1); - $$ = new Scalar\String_(Scalar\String_::parse($1), $attrs); } + | T_CONSTANT_ENCAPSED_STRING { $$ = Scalar\String_::fromString($1, attributes()); } | '"' encaps_list '"' { $attrs = attributes(); $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; parseEncapsed($2, '"', true); $$ = new Scalar\Encapsed($2, $attrs); } diff --git a/grammar/phpyLang.php b/grammar/phpyLang.php index 1a9808dcf5..663c2a144c 100644 --- a/grammar/phpyLang.php +++ b/grammar/phpyLang.php @@ -128,14 +128,6 @@ function($matches) { . ' 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 ('prependLeadingComments' === $name) { assertArgs(1, $args, $name); diff --git a/lib/PhpParser/Node/Scalar/String_.php b/lib/PhpParser/Node/Scalar/String_.php index 8a6d93a474..6690a16bfb 100644 --- a/lib/PhpParser/Node/Scalar/String_.php +++ b/lib/PhpParser/Node/Scalar/String_.php @@ -42,6 +42,22 @@ public function getSubNodeNames() : array { return ['value']; } + /** + * @param bool $parseUnicodeEscape Whether to parse PHP 7 \u escapes + */ + public static function fromString(string $str, array $attributes = [], bool $parseUnicodeEscape = true): self + { + $attributes['kind'] = ($str[0] === "'" || ($str[1] === "'" && ($str[0] === 'b' || $str[0] === 'B'))) + ? Scalar\String_::KIND_SINGLE_QUOTED + : Scalar\String_::KIND_DOUBLE_QUOTED; + + $attributes['rawValue'] = $str; + + $string = self::parse($str, $parseUnicodeEscape); + + return new self($string, $attributes); + } + /** * @internal * diff --git a/lib/PhpParser/Parser/Php5.php b/lib/PhpParser/Parser/Php5.php index 9ab8d6b9bd..d9c8fe0494 100644 --- a/lib/PhpParser/Parser/Php5.php +++ b/lib/PhpParser/Parser/Php5.php @@ -2147,8 +2147,7 @@ protected function initReduceCallbacks() { $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 392 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(4-1)][0] === "'" || ($this->semStack[$stackPos-(4-1)][1] === "'" && ($this->semStack[$stackPos-(4-1)][0] === 'b' || $this->semStack[$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[$stackPos-(4-1)]), $attrs), $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); + $this->semValue = new Expr\ArrayDimFetch(Scalar\String_::fromString($this->semStack[$stackPos-(4-1)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes), $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); }, 393 => function ($stackPos) { $this->semValue = new Expr\ArrayDimFetch($this->semStack[$stackPos-(4-1)], $this->semStack[$stackPos-(4-3)], $this->startAttributeStack[$stackPos-(4-1)] + $this->endAttributes); @@ -2278,8 +2277,7 @@ protected function initReduceCallbacks() { $this->semValue = Scalar\DNumber::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 435 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$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[$stackPos-(1-1)], false), $attrs); + $this->semValue = Scalar\String_::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes, false); }, 436 => function ($stackPos) { $this->semValue = new Scalar\MagicConst\Line($this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); diff --git a/lib/PhpParser/Parser/Php7.php b/lib/PhpParser/Parser/Php7.php index 4eb291ea5f..d469fac523 100644 --- a/lib/PhpParser/Parser/Php7.php +++ b/lib/PhpParser/Parser/Php7.php @@ -2543,8 +2543,7 @@ protected function initReduceCallbacks() { $this->semValue = $this->semStack[$stackPos-(1-1)]; }, 512 => function ($stackPos) { - $attrs = $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes; $attrs['kind'] = ($this->semStack[$stackPos-(1-1)][0] === "'" || ($this->semStack[$stackPos-(1-1)][1] === "'" && ($this->semStack[$stackPos-(1-1)][0] === 'b' || $this->semStack[$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[$stackPos-(1-1)]), $attrs); + $this->semValue = Scalar\String_::fromString($this->semStack[$stackPos-(1-1)], $this->startAttributeStack[$stackPos-(1-1)] + $this->endAttributes); }, 513 => function ($stackPos) { $attrs = $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes; $attrs['kind'] = Scalar\String_::KIND_DOUBLE_QUOTED; diff --git a/test/PhpParser/Node/Scalar/StringTest.php b/test/PhpParser/Node/Scalar/StringTest.php index 814a7758fc..18ba50c2fa 100644 --- a/test/PhpParser/Node/Scalar/StringTest.php +++ b/test/PhpParser/Node/Scalar/StringTest.php @@ -2,8 +2,28 @@ namespace PhpParser\Node\Scalar; +use PhpParser\Node\Stmt\Echo_; +use PhpParser\ParserFactory; + class StringTest extends \PHPUnit\Framework\TestCase { + public function testRawValue() + { + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + $nodes = $parser->parse('assertInstanceOf(Echo_::class, $echo); + + /** @var Echo_ $echo */ + $string = $echo->exprs[0]; + $this->assertInstanceOf(String_::class, $string); + + /** @var String_ $string */ + $this->assertSame('sequence A', $string->value); + $this->assertSame('"sequence \\x41"', $string->getAttribute('rawValue')); + } + /** * @dataProvider provideTestParseEscapeSequences */ diff --git a/test/PhpParser/NodeAbstractTest.php b/test/PhpParser/NodeAbstractTest.php index 902a7436a9..b4d53d8d15 100644 --- a/test/PhpParser/NodeAbstractTest.php +++ b/test/PhpParser/NodeAbstractTest.php @@ -297,7 +297,8 @@ function functionName(&$a = 0, $b = 1.0) { "attributes": { "startLine": 5, "endLine": 5, - "kind": 1 + "kind": 1, + "rawValue": "'Foo'" } } ], @@ -452,7 +453,8 @@ function functionName(&$a = 0, $b = 1.0) { "attributes": { "startLine": 5, "endLine": 5, - "kind": 1 + "kind": 1, + "rawValue": "'Foo'" }, "value": "Foo" }