Skip to content

Curlies around variable names in encapsulated strings are mishandled #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jj101k opened this issue Jan 20, 2017 · 2 comments
Closed
Assignees
Milestone

Comments

@jj101k
Copy link
Contributor

jj101k commented Jan 20, 2017

"use strict";
var engine = require('php-parser');
var parser = new engine({});
var AST1 = parser.parseEval('echo "Hello ${world}";');
console.log(AST1.children[0].arguments[0].value[1]);
var AST2 = parser.parseEval('echo "Hello " . ${world};');
console.log(AST2.children[0].arguments[0].right);

Output is:

Variable {
  kind: 'variable',
  name: Variable { kind: 'variable', name: 'orld', byref: false },
  byref: false }
Variable {
  kind: 'variable',
  name: 
   ConstRef {
     kind: 'constref',
     name: Identifier { kind: 'identifier', resolution: 'uqn', name: 'world' } },
  byref: false }

The two items should be the same. Presumably php-parser is treating it like an explicit interpolated expression (eg. {$world}) by skipping the first character.

@ichiriac
Copy link
Member

Thanks @jj101k for reporting this issue. You're totally right, I've didn't saw it.

The PHP might interpret as this (did not dig enough) :

Variable {
  kind: 'variable',
  name: String{ kind: 'string', name: 'world' },
  byref: false }

BUT, this is a special case in PHP, because the real syntax should be :

Variable {
  kind: 'variable',
  name: 
   ConstRef {
     kind: 'constref',
     name: Identifier { kind: 'identifier', resolution: 'uqn', name: 'world' } },
  byref: false }

Because the syntax is ${expr} where the variable name is resolved by the expression. Here world is considered as a constant, and if it's not defined, PHP is using its name as contents, so resolves it as $world. But as the constant is not defined, if PHP follows strictly these would result in notices errors as the constant is not defined.

I'm saying this because of this behavior :

$> php -r "define(\"world\", \"foo\"); \$foo = \"foobar\"; echo \"hello \${ world }\";"
>> hello foobar

See the extra white spaces around world

$> php -r "define(\"world\", \"foo\"); \$foo = \"foobar\"; echo \"hello \${world}\";"
>> PHP Notice:  Undefined variable: world in Command line code on line 1
>> PHP Stack trace:
>> PHP   1. {main}() Command line code:0
>> hello

Here world is not resolved as a constant but as directly the variable name. The tokens from lexer may also differ :

${world} gives : T_DOLLAR_OPEN_CURLY_BRACES"${" T_STRING_VARNAME"world" "}"
${ world } gives : T_DOLLAR_OPEN_CURLY_BRACES"${" T_WHITESPACE" " T_STRING"world" T_WHITESPACE" " "}"


BUT, as usual in PHP internal, it's not that easy, because the T_STRING_VARNAME is only true with encapsed string. Here the behavior outside an string :

$> php -r "echo \${ world };"
PHP Notice:  Use of undefined constant world - assumed 'world' in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP Notice:  Undefined variable: world in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0

And the result without extra spaces around world :

$> php -r "echo \${world};"
PHP Notice:  Use of undefined constant world - assumed 'world' in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0
PHP Notice:  Undefined variable: world in Command line code on line 1
PHP Stack trace:
PHP   1. {main}() Command line code:0

So, to conclude, you're right I've got multiple bugs on this, but no, "${world}" is not the same as ${world} because it's encapsulated into a string and treated differently, but "${ world }" should be the same as ${world}


Expected output :

"${world}" is the same as "${\"world\"}"

{ kind: 'variable', name: { kind: 'string', value: 'world' } ...

"${ world }" is the same as ${world}

{ kind: 'variable', name: { kind: 'constref', name: { kind: 'identifier', name: 'world' } ...

@ichiriac ichiriac self-assigned this Jan 20, 2017
@ichiriac ichiriac modified the milestones: 1.0.0, 1.1.0 Jan 20, 2017
@ichiriac ichiriac modified the milestones: 2.0.0-pre, 1.1.0 Mar 4, 2017
@ichiriac
Copy link
Member

ichiriac commented Mar 4, 2017

The ouput is now corrected. The first char is no more stripped. The behavior is the same as PHP.


PHP Script :

<?php
echo "Hello ${world}";
echo "Hello $world";
echo "Hello ${ world }";
echo "Hello " . ${world};

AST Structure :

{
  "kind": "program",
  "children": [
    {
      "kind": "echo",
      "arguments": [
        {
          "kind": "encapsed",
          "value": [
            {
              "kind": "string",
              "value": "Hello ",
              "isDoubleQuote": false
            },
            {
              "kind": "variable",
              "name": {
                "kind": "variable",
                "name": "world",
                "byref": false
              },
              "byref": false
            }
          ],
          "type": "string"
        }
      ]
    },
    {
      "kind": "echo",
      "arguments": [
        {
          "kind": "encapsed",
          "value": [
            {
              "kind": "string",
              "value": "Hello ",
              "isDoubleQuote": false
            },
            {
              "kind": "variable",
              "name": "world",
              "byref": false
            }
          ],
          "type": "string"
        }
      ]
    },
    {
      "kind": "echo",
      "arguments": [
        {
          "kind": "encapsed",
          "value": [
            {
              "kind": "string",
              "value": "Hello ",
              "isDoubleQuote": false
            },
            {
              "kind": "variable",
              "name": {
                "kind": "constref",
                "name": {
                  "kind": "identifier",
                  "resolution": "uqn",
                  "name": "world"
                }
              },
              "byref": false
            }
          ],
          "type": "string"
        }
      ]
    },
    {
      "kind": "echo",
      "arguments": [
        {
          "kind": "bin",
          "type": ".",
          "left": {
            "kind": "string",
            "value": "Hello ",
            "isDoubleQuote": true
          },
          "right": {
            "kind": "variable",
            "name": {
              "kind": "constref",
              "name": {
                "kind": "identifier",
                "resolution": "uqn",
                "name": "world"
              }
            },
            "byref": false
          }
        }
      ]
    }
  ],
  "errors": []
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants