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.
98 lines
5.6 KiB
98 lines
5.6 KiB
# Python Tools Submodule
|
|
|
|
TypePHP integrates the Python IDE helper generator and the Python source converter into `tpc`. Both are located in an independent
|
|
`src/PythonTools` directory, only reusing the `tpc` command entry point, and do not enter the normal PHP preprocessing, C++ generation, and compilation pipeline.
|
|
|
|
## Python namespace IDE helper
|
|
|
|
```shell
|
|
./tpc --gen-python-helper math
|
|
./tpc --gen-python-helper numpy.linalg
|
|
./tpc --gen-python-helper numpy --output-dir .ide-helper
|
|
```
|
|
|
|
The command imports the specified Python module through PHPy, and uses the Python `inspect` API to collect functions, parameters, classes, methods, and
|
|
module attributes. The PHPy extension and the target Python module must be installed in the host environment where `tpc` is executed.
|
|
|
|
By default, generated files are located in `ide-helper` in the current directory. `--output-dir` can replace this output root directory, supporting both
|
|
paths relative to the current directory and absolute paths:
|
|
|
|
```text
|
|
ide-helper/python/math.php
|
|
ide-helper/python/numpy/linalg.php
|
|
ide-helper/python.php
|
|
ide-helper/PyObject.php
|
|
```
|
|
|
|
Each time a module helper is generated, the Python `builtins` are also scanned and the root namespace file
|
|
`python.php` is generated, providing IDE completion for builtin symbols such as `python\tuple()` and `python\len()`. This file is
|
|
regenerated according to the current Python environment.
|
|
|
|
When a module helper is first generated, the common `PyObject.php` is also generated. It contains method hints for dynamic access, calls,
|
|
array access, iteration, and `toArray()`, `toValue()` of `PyObject`, shared by all Python module helpers. If this file
|
|
already exists, the generator keeps the original file and does not overwrite it.
|
|
|
|
Generated content uses TypePHP's module-as-namespace form, e.g. `python\math\sqrt()`, and is compatible with the IDE name resolution of ordinary
|
|
`use`, `use function`, and `use const`. The end of the file contains `die`, which is used to explicitly
|
|
terminate the program when executed by mistake. Helpers can only be handed to the IDE for indexing; they cannot be included, nor added to a TypePHP project's sources or compilation inputs.
|
|
|
|
`PyObject::IDE_HELPER_ONLY` is a hint constant shared by all helpers. The method bodies of non-`void` stubs use
|
|
`die(\PyObject::IDE_HELPER_ONLY)` to satisfy the IDE's control-flow checks on return types, no longer producing a "missing return
|
|
statement" diagnostic. Module attributes use namespace `const` declarations, supporting the IDE's constant completion and `use const`.
|
|
PHP 8.1 and above allow `new` in constant initializer expressions. Module attributes therefore directly use an IDE-analysis-only
|
|
`PyObject` instance as a placeholder value:
|
|
|
|
```php
|
|
const pi = new \PyObject();
|
|
```
|
|
|
|
This way the IDE precisely recognizes the constant as `PyObject`, instead of inferring a wrong type from `null`.
|
|
|
|
The common `PyObject` helper also declares TypePHP's virtual keyword methods, including `toInt()`, `toFloat()`,
|
|
`toString()`, `toBool()`, `toStream()`, high-precision type conversions, `toObject()`, `toAny()`, and `toRef()`.
|
|
These declarations are only used for IDE completion; calls are expanded at compile time and are not entity methods of the PHPy `PyObject` runtime class.
|
|
`toArray()` and `toValue()` are still real methods provided by PHPy.
|
|
|
|
Python class constructors explicitly call `parent::__construct()`. If a Python object defines `count()`, the helper
|
|
does not declare it again, because `PyObject::count(): int` is already used for PHP `Countable`. When the Python object's own
|
|
`count()` needs to be called, it should be written explicitly as `$object->__call('count', $arguments)`.
|
|
|
|
PHP function/class names are case-insensitive, while Python names are case-sensitive; PHP reserved words also cannot be declared as ordinary
|
|
stub symbols. The generator reports symbols that cannot be expressed with legal PHP declarations as comments, and does not rename the Python API on its own.
|
|
The call syntax of `python\print()` is legal, but PHP forbids declaring a function named `print`, so a plain
|
|
PHP helper file cannot provide a syntax-error-free symbol declaration for it. Reserved words such as `list`, `int`, and `float` have
|
|
the same limitation.
|
|
|
|
## Python to TypePHP
|
|
|
|
```shell
|
|
./tpc --convert-python-to-php script.py > script.php
|
|
```
|
|
|
|
The converter invokes `python3` in the PATH to parse the Python AST, then outputs PHP source using the TypePHP Python namespace
|
|
syntax. Ordinary module imports are converted to namespace imports:
|
|
|
|
```python
|
|
import math
|
|
print(math.sqrt(16))
|
|
```
|
|
|
|
```php
|
|
use python\math;
|
|
|
|
function main(): void
|
|
{
|
|
python\print(math\sqrt(16));
|
|
}
|
|
```
|
|
|
|
Currently it supports ordinary imports, functions, assignments, calls, container literals, basic operations, single comparisons, if/while/for,
|
|
lambda, and basic f-strings. Module top-level variables are converted to PHP globals to preserve the ability of functions to read module variables.
|
|
When semantics can be strictly preserved, the converter directly uses PHP native syntax: `print()` with no arguments or safely convertible arguments
|
|
generates `echo` with a newline, and `sys.exit()` with integer literal exit codes generates `exit`. `print()` with
|
|
`sep`, `end`, `file`, or `flush` arguments, and `sys.exit()` with strings or objects,
|
|
are not fully consistent with PHP behavior and remain as Python calls.
|
|
|
|
The converter follows the principle of "reject when semantics cannot be reliably preserved". Not-yet-completed syntax such as class, async, generator, try/with, decorator,
|
|
destructuring assignment, chained comparison, nested functions, and loop-else throws an error with the
|
|
source file and line number, rather than generating PHP code that looks usable but is semantically wrong.
|
|
|