PHP って JavaScript に変換できるの？できるわけないだろ！ babel-preset-php ってのが今日リリースされた？これまさか・・・。ファーーーーーーーーーーーwwwwwwwwwwww


PHP (7) を Javascript (ES7) に変換するための Babel プリセット [babel-preset-php](https://gitlab.com/kornelski/babel-preset-php "Kornel / babel-preset-php · GitLab") が本日 (2017-7-12) リリースされたみたいです:dizzy_face::sweat_drops:

正直、「は？凄すぎだろ (小並感) 」という言葉しか出てきませんww

とりあえず面白そうなので試してみます！

## 使い方

公式の通りですが、プロジェクトを作成し `babel-cli` と `babel-preset-php` をインストールします。

```
cd path/to/project
npm init -y
npm i -g babel-cli
npm i -S babel-preset-php
```

_.babelrc_ を作成します。

```file:.babelrc
{
  "presets": ["php"]
}
```

サンプルの PHP ファイルとして _file.php_ を作成します。

```php:file.php
<?php
define('FOO', max(floatval($c), strlen("foo")));
$bar['x'][][$b] = json_encode(__FILE__);
class Foo extends Bar\Baz {
  var $z = "hello" . "world";
  function __construct($some = array(7)) {
    parent::__construct(func_get_args());
    self::${$k} = "{$this->z[10]}";
  }
}
```

準備ができました！

後は以下のように PHP から JavaScript にトランスパイルすると、

```
babel file.php -o file.js
```

PHP ファイルが Babel によって変換された以下のような Javascript ファイルが作成されます。

```js:file.js
const FOO = Math.max(+c, "foo".length);
bar.x.push({
  [b]: JSON.stringify(__filename)
});

class Foo extends Bar.Baz {
  constructor(some = [7]) {
    super(arguments);
    this.z = "hello" + "world";
    this.constructor[k] = `${this.z[10]}`;
  }

};
```

凄すぎる・・・:joy:

[サポートされていない言語機能](https://gitlab.com/kornelski/babel-preset-php#unsupported-language-features) 等もあるのでもっと詳しく知りたい方は公式を参照してください。

## もっと試す！

毎回複数の PHP ファイルを JavaScript ファイルへ `babel-cli` で叩いてトランスパイルするのが面倒だったため、一括でトランスパイル出来るよう以下のプロジェクトを作成しました:thumbsup:

[kotarella1110/babel-preset-php-sample](https://github.com/kotarella1110/babel-preset-php-sample "kotarella1110/babel-preset-php-sample")

以下では、変換前の PHP ファイルと変換後の JavaScript ファイルを表示しています。実際に変換されたファイルを確認したい場合は、上記のプロジェクトをクローンして `yarn build` を実行することで確認できます:laughing:

試してみた感想としては、 **想像より言語機能をサポートしていて凄い** と思いました。さっきから凄いしか言ってないww

### AST translation

#### Expressions

```php:src/translation/expressions.php
<?php
2 + 2;
$a = null;
$a .= 'x';
$a = NuLL;
$a + 1;
$a = $b = 1;
$c = $d ? 1 : 2;
$d ?: 2;
$a[1] + $b[Z];
$b[$foo['Z']];
"f\\no\\\\o";
bar();
eval($lol);
(int)"1";
(float)"1";
(bool)"1";
(string)$x;
(object)$x;
(array)$x;
```

```js:dist/translation/expressions.js
var b;
2 + 2;
var a = undefined;
a += "x";
a = undefined;
a + 1;
a = b = 1;
var c = d ? 1 : 2;
d ? d : 2;
a[1] + b[Z];
b[foo.Z];
"f\\no\\\\o";
bar();
eval(lol);
+"1";
+"1";
!!"1";
String(x);
Object(x);
Array.from(x);
```

#### Builtins

```php:src/translation/builtins.php
<?php
echo "foo",$bar;
print "foo";
isset($x[$y]);
isset($x[$y], $z);
empty($x[$y]);
unset($x[$y]);
function is_bool(){}; is_bool($x);
__FILE__;
__DIR__;
function foo(){__FUNCTION__;}
__LINE__;
__LINE__;
```

```js:dist/translation/builtins.js
echo("foo", bar);
print("foo");
undefined !== x[y];
undefined !== x[y] && undefined !== z;
!x[y];
delete x[y];

function is_bool() {};

is_bool(x);
__filename;
__dirname;

function foo() {
  "foo";
};

12;
13;
```

#### Array and list

```php:src/translation/arrayAndList.php
<?php
$a = []; $a['x'] = 2;
$a = [1,2]; $a = 2;
$a = ['x' => 1];
$a = [' ' => 1];
$a = [$z => 1];
$a = [$z => 1, 'a', 'b'];
$a = ['a', $z => 1, 'b'];
$a = ['a', 3 => 'c', 'b'];
list($a, $b) = [1,2];
list($a,,$b) = [1,2];
$a[] = 1;
foo($a['b'][]);
$a[1][] = 1;
$a['b']['c'][]['d']['e'] = 1;
$a[CON] = 1;
$a->foo[] = 1;
$a->{$foo}[] = 1;
```

```js:dist/translation/arrayAndList.js
var b;
var a = Array();
a.x = 2;
a = [1, 2];
a = 2;
a = {
  x: 1
};
a = {
  " ": 1
};
a = {
  [z]: 1
};
a = {
  [z]: 1,
  0: "a",
  1: "b"
};
a = {
  0: "a",
  [z]: 1,
  1: "b"
};
a = {
  0: "a",
  3: "c",
  4: "b"
};
[a, b] = [1, 2];
[a,, b] = [1, 2];
a.push(1);
foo(a.b);
a[1].push(1);
a.b.c.push({
  d: {
    e: 1
  }
});
a[CON] = 1;
a.foo.push(1);
a[foo].push(1);
```

#### Templates

```php:src/translation/templates.php
<?php
"foo$bar";
"foo$bar baz";
"${not_varvar}";
"foo${bar}$quz";
"foo${2+2}";
$$var;
"foo${$varvar}";
```

```js:dist/translation/templates.js
`foo${bar}`;
`foo${bar} baz`;
`${not_varvar}`;
`foo${bar}${quz}`;
`foo${global[2 + 2]}`;
global[var];
`foo${global[varvar]}`;
```

#### Functions

```php:src/translation/functions.php
<?php
function bar($foo) {return $foo + 1;}
function bar($foo) {$foo = 2;}
function bar(TypeName $foo) {$foo = 2;}
$a=1; function bar() {$a = 2;}
$z = function() use($x){$x=1;$y=2;};
$z = function(){static $n;};
if ($a = 1) {if ($b = 2 && $c = 2) {}}
```

```js:dist/translation/functions.js
function bar(foo) {
  return foo + 1;
};

function bar(foo) {
  foo = 2;
};

function bar(foo: TypeName) {
  foo = 2;
};

var a = 1;

function bar() {
  var a = 2;
};

var z = () => {
  x = 1;
  var y = 2;
};

z = () => {
  if (!("_static_closure_1_n" in global)) _static_closure_1_n = undefined;
};

if (a = 1) {
  var b, c;

  if (b = 2 && (c = 2)) {}
}
```

#### Loops

```php:src/translation/loops.php
<?php
for($x=1; $x < 10; $x++, $y++){}
foreach($arr as $el){}
foreach([1,2,3] as $el){}
foreach($arr as $k => $v){}
foreach(func() as $k => $v){}
while(true) { $a--; break; }
do{if(false)continue;}while(true);
```

```js:dist/translation/loops.js
for (var x = 1; x < 10; x++, y++) {}

for (var el of Object.values(arr)) {}

for (var el of [1, 2, 3]) {}

for (var k in arr) {
  var v = arr[k];
}

{
  let _tmp_0 = func();

  for (var k in _tmp_0) {
    var v = _tmp_0[k];
  }
}

while (true) {
  a--;
  break;
}

do {
  if (false) continue;
} while (true);
```

#### Statements

```php:src/translation/statements.php
<?php
switch($c) {case "a": case "b": return $c; default: return $z;}
require "foo";
include_once "foo";
if (false) while(true) { $a--; }
if (false) if (true) { --$a; }
exit(1);
```

```js:dist/translation/statements.js
switch (c) {
  case "a":
  case "b":
    return c;

  default:
    return z;
}

require("foo");

require("foo");

if (false) while (true) {
  a--;
}
if (false) if (true) {
  --a;
}
throw die(1);
```

#### Exceptions

```php:src/translation/exceptions.php
<?php
try {hey();} catch(Exception $e) {bye($e);}
try{}catch(\Exception $e){}
throw new Error();
try{}catch(Foo $e){$a=1;}
function foo(){static $z; try{}catch(Foo $f){$f=1;}catch(Bar $b){$b=1;$z=1;}}
try{}catch(Foo | Bar $e){$a=1;}
try{}catch(Foo $e){}catch(\Exception $z){$a=1;}
```

```js:dist/translation/exceptions.js
try {
  hey();
} catch (e) {
  bye(e);
}

try {} catch (e) {}

throw new Error();

try {} catch (e) {
  if (e instanceof Foo) {
    var a = 1;
  }
}

function foo() {
  if (!("_static_foo_z" in global)) _static_foo_z = undefined;

  try {} catch (f) {
    if (f instanceof Foo) {
      f = 1;
    } else if (f instanceof Bar) {
      f = 1;
      _static_foo_z = 1;
    }
  }
};

try {} catch (e) {
  if (e instanceof Foo || e instanceof Bar) {
    var a = 1;
  }
}

try {} catch (e) {
  if (e instanceof Foo) {} else if (true) {
    var a = 1;
  }
}
```

#### Static

```php:src/translation/static.php
<?php
function foo() {static $bar; $bar=1;}
function foo() {global $bar; $bar=1;}
function foo() {static $bar,$baz=1; $bar=1;}
```

```js:dist/translation/static.js
function foo() {
  if (!("_static_foo_bar" in global)) _static_foo_bar = undefined;
  _static_foo_bar = 1;
};

function foo() {
  if (!("bar" in global)) bar = undefined;
  bar = 1;
};

function foo() {
  {
    if (!("_static_foo_bar" in global)) _static_foo_bar = undefined;
    if (!("_static_foo_baz" in global)) _static_foo_baz = 1;
  }
  _static_foo_bar = 1;
};
```

#### Class

```php:src/translation/class.php
<?php
new Foo($bar);
class Foo {};
class Foo2 extends Bar {};
class Foo3 extends Foo\Bar {};
class Foo4 extends Foo\Bar\Baz\Quz {};
class Foo5 extends \Foo\Bar {};
class Foo6 {function __construct($bla){$this->foo=1;}};
class Foo7 {function __construct($bla){$this->${foo}=1;}};
class Foo8 {function __construct($bla){parent::foo();}};
class Foo9 {function __construct($bla){parent::__construct();}};
class Foo10 {function bar($z){$this->{$meta} = 2;}};
class Foo11 {static function bar($z){self::bar();}};
class Foo12 {function bar(){self::CON;}};
class Foo13 {private function bar($z=1){} protected function quz(){$this->bar();}};
```

```js:dist/translation/class.js
new Foo(bar);

class Foo {};

class Foo2 extends Bar {};

class Foo3 extends Foo.Bar {};

class Foo4 extends Foo.Bar.Baz.Quz {};

class Foo5 extends global.Foo.Bar {};

class Foo6 {
  constructor(bla) {
    this.foo = 1;
  }

};

class Foo7 {
  constructor(bla) {
    this[foo] = 1;
  }

};

class Foo8 {
  constructor(bla) {
    super.foo();
  }

};

class Foo9 {
  constructor(bla) {
    super();
  }

};

class Foo10 {
  bar(z) {
    this[meta] = 2;
  }

};

class Foo11 {
  static bar(z) {
    this.bar();
  }

};

class Foo12 {
  bar() {
    this.constructor.CON;
  }

};

class Foo13 {
  bar(z = 1) {}

  quz() {
    this.bar();
  }

};
```

#### Class props

```php:src/translation/classProps.php
<?php
class Foo {const Z=1;}
class Foo2 {static $z=1;}
class Foo3 {private static $z=1; static function x(){self::$z;self::${z()};}}
class Foo4 {var $z=1;}
class Foo5 extends Bar {var $z=1;}
class Foo6 extends Bar {var $z=1; function __construct(){hi();}}
class Foo7 extends Bar {var $z=1; function __construct(){parent::__construct();hi();}}
```

```js:dist/translation/classProps.js
class Foo {
  static Z = 1;
};

class Foo2 {
  static z = 1;
};

class Foo3 {
  static z = 1;

  static x() {
    this.z;
    this[z()];
  }

};

class Foo4 {
  constructor() {
    this.z = 1;
  }

};

class Foo5 extends Bar {
  constructor() {
    super(...arguments);
    this.z = 1;
  }

};

class Foo6 extends Bar {
  constructor() {
    this.z = 1;
    hi();
  }

};

class Foo7 extends Bar {
  constructor() {
    super();
    this.z = 1;
    hi();
  }

};
```

#### Types

```php:src/translation/types.php
<?php
class Foo {function annotated($untyped, Cls\Name $class, self $self, array $array, callable $callable, bool $bool, float $float, int $int, string $string, iterable $iter) {}}
class Foo2 {function annotated($untyped = null, Cls\Name $class = null, self $self = null, array $array = null, callable $callable = null, bool $bool = null, float $float = null, int $int = null, string $string = null, iterable $iter = null) {}}
class Foo3 {function annotated($untyped, ?Cls\Name $class, ?self $self, ?array $array, ?callable $callable, ?bool $bool, ?float $float, ?int $int, ?string $string, ?iterable $iter) {}}
```

```js:dist/translation/types.js
class Foo {
  annotated(untyped, class: Cls.Name, self: Foo, array: {} | any[], callable: Function, bool: boolean, float: number, int: number, string: string, iter: {} | any[]) {}

};

class Foo2 {
  annotated(untyped = undefined, class: ?Cls.Name = undefined, self: ?Foo2 = undefined, array: ?{} | any[] = undefined, callable: ?Function = undefined, bool: ?boolean = undefined, float: ?number = undefined, int: ?number = undefined, string: ?string = undefined, iter: ?{} | any[] = undefined) {}

};

class Foo3 {
  annotated(untyped, class: ?Cls.Name, self: ?Foo3, array: ?{} | any[], callable: ?Function, bool: ?boolean, float: ?number, int: ?number, string: ?string, iter: ?{} | any[]) {}

};
```

### AST modification

#### Expressions

```php:src/modification/expressions.php
<?php
is_string($x);
is_bool($x);
```

```js:dist/modification/expressions.js
"string" === typeof x;
"boolean" === typeof x;
```

#### Builtins

```php:src/modification/builtins.php
<?php
is_nan($x);
is_float($x);
is_object($x);
is_array($x);
trim($x);
function trim(){}; trim($x);
ord($x);
ord($x["z"]);
ord($x[5]);
chr($x);
substr($x,1,2);
str_replace(1,2,$x);
str_replace(1,2,$x,1);
str_replace(1,2,$x,$z);
trim($x,"x");
$_ENV["X"];
```

```js:dist/modification/builtins.js
Number.isNaN(x);
"number" === typeof x;
"object" === typeof x;
Array.isArray(x);
trim(x);

function trim() {};

trim(x);
x.charCodeAt(0);
x.z.charCodeAt(0);
x.charCodeAt(5);
String.fromCharCode(x);
x.substr(1, 2);
str_replace(1, 2, x);
x.replace(1, 2);
str_replace(1, 2, x, z);
trim(x, "x");
process.env.X;
```

#### Preg

```php:src/modification/preg.php
<?php
preg_replace("#t\\*ex*t#i", $y, $z);
preg_replace_callback("/(reg)/", $y, $z);
preg_replace("$dynamic", $y, $z);
```

```js:dist/modification/preg.js
z.replace(/t\*ex*t/gi, y);
z.replace(/(reg)/g, y);
preg_replace(`${dynamic}`, y, z);
```

#### Array

```php:src/modification/array.php
<?php
array_key_exists($k,$a);
in_array($needle, $haystack);
```

```js:dist/modification/array.js
k in a;
-1 !== haystack.indexOf(needle);
```

#### Define

```php:src/modification/define.php
<?php
defined('foo');
defined('%z');
define('foo', 2);
define('1foo', 2);
function b(){define('foo', 2);}
function b(){define('foo bar', 2);}
define("foo$bar", "$baz quz");
```

```js:dist/modification/define.js
"undefined" !== typeof foo;
undefined !== global["%z"];
const foo = 2;
global["1foo"] = 2;

function b() {
  global.foo = 2;
};

function b() {
  global["foo bar"] = 2;
};

global[`foo${bar}`] = `${baz} quz`;
```

#### Functions

```php:src/modification/functions.php
<?php
function bar() {$a = func_get_args();}
```

```js:dist/modification/functions.js
function bar() {
  var a = arguments;
};
```

#### Class

```php:src/modification/class.php
<?php
class Foo extends Exception {}
```

```js:dist/modification/class.js
class Foo extends Error {};
```

#### Standard funcs

```php:src/modification/standardFuncs.php
<?php
count($a);
function count() {}; count($a);
sort($a);
usort($a, function($a,$b){
  return 1;}
);
array_pop($a);
array_keys($a);
array_values($a);
array_shift($a);
pow(1);
max(1,2);
$max = function(){}; max(1,2);
array_unshift($a,$b,$c);
array_push($a,1,2+2,3);
array_slice($a,1,2);
array_reverse($a);
array_splice($a,1,2,$z);
array_walk($a,$z,1);
array_reduce($a,function(){});
array_filter($a,function(){});
array_map(function(){},$a);
implode('str', $a);
explode('str', $a, 2);
function_exists('bla');
function_exists($z);
class_exists($z);
```

```js:dist/modification/standardFuncs.js
count(a);

function count() {};

count(a);
a.sort();
a.sort((a, b) => {
  return 1;
});
a.pop();
Object.keys(a);
Object.values(a);
a.shift();
Math.pow(1);
max(1, 2);

var max = () => {};

max(1, 2);
a.unshift(b, c);
a.push(1, 2 + 2, 3);
a.slice(1, 2);
a.reverse();
a.splice(1, 2, z);
a.forEach(z, 1);
a.reduce(() => {});
a.filter(() => {});
a.map(() => {});
a.join("str");
a.split("str", 2);
"function" === typeof bla;
"function" === typeof global[z];
"function" === typeof global[z];
```

## え？これどこで使えるの？

**マジで分かりませんww** が、 AWS Lambda を PHP で書いたぞ！:sunglasses:とドヤることぐらいは出来るかもしれませんww