PHP source code transformations operating on abstract syntax tree (AST). This library contains multiple visitors that convert AST of PHP7.1 to PHP7.0-compatible one. Alongside this each release comes with a binary with console utility that transpiles whole worktree in-place.
bash <(curl -sSL 'https://raw.githubusercontent.com/Mikulas/transpiler/v1.1.2/installer.sh')
Download release binary from https://github.com/Mikulas/transpiler/releases/latest,
put it anywhere in your $PATH
and make it executable.
TARGET="/usr/local/bin/php-transpiler"
curl -L 'https://github.com/Mikulas/transpiler/releases/download/v1.1.2/transpiler.phar' -o "$TARGET"
chmod a+x "$TARGET"
export PATH="$PATH:/usr/local/bin"
This library by itself only contains AST modifiers, to convert your codebase, you probably want
the console utility instead. If you really need to build on this, add mikulas/transpiler
as dependency:
composer require mikulas/transpiler
php-transpile <paths> (<paths>)... [--verbose]
This command will convert all .php
and .phpt
files found recursively in paths
.
Make sure you run this command only when you have no changes in your worktree, otherwise
you will lose those changes! Files are transpiled in-place.
By transpiling code to older runtime versions you gain many benefits of PHP 7.1 such as void return types and optionals (which are mostly useful for static analysis), but you are not forced to update production to unstable bleeding-edge versions.
function foo(): void
{
}
# -->
function foo()
{
}
Limitations: ReflectionFunctionAbstract::getReturnType()
and hasReturnType()
will return NULL
and FALSE
respectively instead of original values.
class Foo
{
public const A = 'a';
protected const B = 'b';
private const C = 'c';
}
# -->
class Foo
{
const A = 'a';
const B = 'b';
const C = 'c';
}
Limitations: none
['X' => ['A' => $a, 'B' => $b]] = ['X' => ['A' => 10, 'B' => 20]];
list('T' => list('U' => $u, 'V' => $v)) = ['T' => ['U' => 15, 'V' => 19]];
while (list($a, $b) = $right) {
block();
}
# -->
${'~transpiler-1'} = ['X' => ['A' => 10, 'B' => 20]];
$a = ${'~transpiler-1'}['X']['A'];
$b = ${'~transpiler-1'}['X']['B'];
${'~transpiler-2'} = ['T' => ['U' => 15, 'V' => 19]];
$u = ${'~transpiler-2'}['T']['U'];
$v = ${'~transpiler-2'}['T']['V'];
while (${'~transpiler-3'} = $right) {
$a = ${'~transpiler-3'}[0];
$b = ${'~transpiler-3'}[1];
block();
}
Limitations: slightly worse performance, introduces new variable
Dynamic mutating keys are not supported.
[$x++ => $a, $x++ => $b] = [10, 20];
function sum(?int $a, ?int $b)
{
}
# -->
function sum(int $a = NULL, int $b = NULL)
{
}
Closure::fromCallable('intdiv');
Closure::fromCallable([$foo, 'bar']);
Closure::fromCallable([Foo::class, 'qaz']);
use Closure as Alias;
Alias::fromCallable($foo($a = $b));
# -->
function () {
return call_user_func_array('intdiv', func_get_args());
};
function () use(&$foo) {
return call_user_func_array([$foo, 'bar'], func_get_args());
};
function () {
return call_user_func_array([\Foo::class, 'qaz'], func_get_args());
};
use Closure as Alias;
function () use(&$foo, &$a, &$b) {
return call_user_func_array($foo($a = $b), func_get_args());
};
Limitations: Dynamic invocation does not work (such as from call_user_func
).