TypePHP 编译器 https://swoole.com/aot/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

9.3 KiB

py2php: Python → TypePHP source conversion tool

Usage

./bin/tpc.php --convert-python-to-php examples/python/version.py > examples/python/version.php

The generated PHP source is written to stdout, errors to stderr, with exit code 0 for success / 1 for failure.

Architecture

.py source
  └─ PythonAstLoader      python3 subprocess (ast module) → JSON AST
       └─ PythonToTypePhpConverter   AST → TypePHP source string
            └─ Command::execute      CLI dispatch (--convert-python-to-php)
  • Source: src/PythonTools/Command.php, src/PythonTools/Converter/
  • Unsupported syntax throws RuntimeException("{file}:{line}: unsupported Python syntax {node type}[: details]"), which the CLI layer converts to stderr + exit code 1.
  • Tests: phpunit/src/PythonTools/ (PythonToTypePhpConverterTest, PythonAstLoaderTest, PythonToolsCommandTest), corresponding item by item to this document.

Statement support matrix

Python syntax Status Conversion rule / error
x = expr $x = expr;, module-level variables are automatically injected as global
x = y = 1 (chained assignment) $x = $y = 1; (name targets only; errors on property/subscript targets)
x += expr and other augmented assignments supports the + - * / % ** << >> | ^ & families; //= @= expand to python\operator\floordiv/matmul($x, ...) calls
x: int = expr annotation ignored, converted to an ordinary assignment
x: int (annotation only) converted to comment // annotation-only declaration: x, not registered as a module global
a, b = x (destructuring) [$a, $b] = $x->toArray(); (PyObject converted to PHP array then destructured; elements may be names/properties/subscripts. Nested destructuring, star destructuring a, *b = x, and chained destructuring are not supported. Element-count mismatches fill null per PHP semantics rather than raising Python's ValueError)
def f(...) see "Function signatures"; a function named main is renamed to main_ (to avoid conflict with the TypePHP entry point), and call sites are rewritten accordingly
nested def FunctionDef: nested functions require Python closure scope analysis
@decorator see "Function decorators"
return [expr] return [expr];
if / elif / else isomorphic conversion
while isomorphic conversion; while/else is not supported
for i in iter foreach (iter as $i); for/else and tuple targets are not supported
break / continue / pass pass// pass comment
global x global $x; (when combined with the auto-injected global it appears twice — redundant but valid, a known behavior)
del x / del o.a / del d[k] unset(...); del (a, b) tuple/list targets expanded item by item; invalid del targets (such as del f()) are rejected first by the Python parser
module-level string literal (docstring) converted to /** ... */ comment (*/ escaped as * /)
import a.b use python\a; (only the first segment as the alias, see "Known behaviors")
import a.b as x use python\a\b as x; (as omitted when the alias equals the last segment)
from m import f [as g] call sites mapped to python\m\f(...)
from . import m ImportFrom: relative imports are not supported yet
from m import * ImportFrom: star imports are not supported
class ClassDef
with With
raise / try / assert Raise / Try / Assert
async def / await AsyncFunctionDef (await unreachable, outer level errors first)
match Match
nonlocal Nonlocal

Function signatures

Python form Status TypePHP output
def f(x, y=4) function f($x, $y = 4)
def f(a, *, b) function f($a, $b = null) (keyword-only parameters without defaults are padded with null)
def f(*args) / def f(**kw) function f(...$args)
def f(*a, **kw) FunctionDef: simultaneous *args and **kwargs cannot be represented by one PHP signature
lambda a, b=2: a + b fn ($a, $b = 2) => $a + $b

Expression support matrix

Python syntax Status Conversion rule / error
literals int / float / str / True / False / None var_export; Nonenull
b'...' bytes {file}: Python bytes literals are not supported yet (no line number)
1j complex {file}: Python complex literals are not supported yet (no line number)
variable names $name; this escaped as $this_
module alias as a value a Python module cannot be used as a first-class value in TypePHP namespace syntax
attribute chain o.a.b $o->a->b; for module alias chains only the first segment is a module member: sys.version_info.majorsys\version_info->major
module attribute assignment/deletion Attribute: Python module attributes cannot be assigned or deleted
function call defined functions connect directly f(...); built-ins mapped python\len(...); from m import f mapped python\m\f(...); other names callable as variables $f(...)
keyword arguments / *args / **kwargs calls f(x: 1, ...$args)
container literals [] () {} {:} python\list/tuple/set/dict([...]), supports ... unpacking
binary operators + - * / % ** << >> | ^ & isomorphic conversion
// floor division / @ matrix multiplication python\operator\floordiv(a, b) / python\operator\matmul(a, b)
unary operators - + not ~ - + ! ~
comparisons == != < <= > >= isomorphic conversion
is / is not === / !==
in / not in python\operator\contains(b, a) (arguments swapped) / negated
chained comparison a < b < c Compare: chained comparisons require explicit temporary variables
a and b / a or b BoolOp
x if c else y (c ? x : y)
subscript a[i] / slice a[l:u:s] $a[$i] / $a[python\slice(l, u, s)] (defaults to null)
f-string concatenation + ->toString(); operator-precedence-sensitive expressions are parenthesized as a whole
f-string !r conversion / :03d format spec FormattedValue: formatted f-string conversions are not supported yet
walrus := assignment within expression ($n = 10)
comprehensions / generator expressions ListComp / SetComp / DictComp / GeneratorExp
yield / yield from Yield / YieldFrom

Function decorators

Decorators rebind the function to the same-named module variable at the start of main() (before other top-level statements), bottom-up per Python semantics:

@a
@b
def greet(): ...
function greet() { ... }

function main(): void
{
    global $greet;
    $greet = b('greet');
    $greet = a('greet');
    ...
}
  • A decorator can be a defined function, a from m import f imported symbol, a module attribute, or a decorator factory (@dec('x')$greet = dec('x')('greet');)
  • The decorated function name is registered as a module global, and all call sites (including inside other function bodies) call the decorated result indirectly via global + variable: $greet()
  • Recursive calls inside the decorated function body also resolve to the decorated variable, consistent with Python semantics

print / sys.exit degradation rules

Degrade to native statements only when PHP behavior is fully identical to Python:

Form Output
print() echo "\n";
print("a", "b") (string/integer constants, module attributes, containers, f-strings) echo 'a', ' ', 'b', "\n";
print(1.5), print(True), print(x, sep=...) not degraded: python\print(...)
after user-defined/imported/assigned shadowing of print not degraded
sys.exit() / sys.exit(2) (including the from sys import exit form) exit; / exit(2);
sys.exit("fail") not degraded: sys\exit('fail');

Known behaviors (not errors, but worth noting)

  1. import os.path (no alias) only introduces the first segment use python\os;.
  2. An explicit global x inside a function and the auto-injected global x for a module global appear twice (valid PHP).
  3. Writing print = str (assigning a built-in name to a variable) treats the right side as a variable ($print = $str;), not as built-in name resolution.
  4. Errors for bytes/complex literals have no line number (constants are encoded during the AST load stage; position information is not passed through).
  5. Decorator rebinding uniformly happens at the start of main(), slightly differing from Python's exact "decorated at the def site" position; if a decorator expression depends on assignments later in the top-level statements, the evaluation timing may differ.
  6. Decorated function names are registered as module globals, so the name appears in every function's auto-injected global list (redundant but valid).

Running tests

vendor/bin/phpunit --filter 'PythonToTypePhpConverterTest|PythonAstLoaderTest|PythonToolsCommandTest'

Converter tests depend on a real python3 to parse the AST, and are skipped automatically when the environment lacks it.