endel / js2php

JavaScript (ES6) to PHP source-to-source transpiler.
https://endel.github.io/js2php/
MIT License
333 stars 41 forks source link

access to global scope from functions #24

Closed allain closed 6 years ago

allain commented 6 years ago

Given the following JavaScript the PHP generated was malformed:

const total = 0
function add(x) {
  total += x
}

Produced this PHP:

$total = 0;
function add($x) use (&$total) {
$total += $x;
}

Unfortunately use is not allowed to be used on FunctionDeclarations anything other than an so to make the above work I've made FunctionDeclarations use the global keyword instead:

$total = 0;
function add($x) {
global $total;
$total += $x;
}

At the same time I found a bug with function expressions, as opposed to Arrow Functions that produced the wrong code too. Those now use the use keyword too.

allain commented 6 years ago

I'm pretty sure isn't a good solution. I mean, it produces valid PHP code, but it's not semantically correct.

The following code when run in JS yields hello for example, but would print nothing in PHP.

function a(message) {
  function b() {
    print(message)
  }
  b()
}
a('Hello');

I think if we want PHP interop still (functions defined in JS to be available in PHP) we'll need to transform all nested FunctionDeclarations into FunctionExpressions, prior to generating the PHP

endel commented 6 years ago

Hi @allain, sorry the huge delay to answer here. I wasn't receiving email updates from this repository. Thanks a lot for your time creating this pull-request!

I've just noticed that this problem is quite complex, actually. Even the use (&$total) transformation doesn't work properly when using an AssignmentExpression inside a function block.

JavaScript input:

const total = 0;
function add(x) {
  total += x;
}

PHP output:

$total = 0;
function add($x) {
$total += $x;
}

The problem is that - as functions does create a new scope - when using assignments, the transformation tool assumes immediatelly that it should be assigned as a new variable in the current scope, and not re-used from a previous scope.

A silly workaround for that would be to access the variable first, but that's really odd.

JavaScript input (with workaround)

const total = 0;
function add(x) {
  total
  total += x;
}

PHP output (with correct use)

$total = 0;
function add($x) use (&$total) {
$total;
$total += $x;
}

I understand your proposal here is to allow calling global for these variables, but they're not being added to the scope's using list first, so even if I accept your pull-request, things won't work properly.

allain commented 6 years ago

I agree. Odd and working is fine I suppose, are you just proposing to leave well enough alone?

endel commented 6 years ago

Does this happen only when functions are on the higher scope? I've just pushed an update that should fix on that case!

Let me know if that works for you!

endel commented 6 years ago

Added this test case: https://github.com/endel/js2php/blob/master/test/fixtures/failures.js