Python 在 AI、科学计算领域的生态极其丰富,而 PHP 相关的生态几乎是一片空白。想在 PHP 里做矩阵运算、调用一个训练好的模型、或者复用某个只有 Python 包的算法库,过去几乎只能退回到「用 Python 写一个 HTTP 服务,PHP 再远程调用」——引入一个常驻子进程、一套 RPC、一堆序列化开销和运维成本。
TypePHP 提供了一条更直接的路:语言层面的 Python 互调用。你可以在 TypePHP 源码里直接 import Python 模块、调用 Python 函数和类、操作 Python 对象,两个运行时运行在同一个进程里,参数和返回值在内存里直接转换,不经过 JSON、RPC 或 Python 子进程。
usepython\os;echo os\name, "\n"; // posixecho python\sys\version_info->major, "\n"; // 3
本文基于 TypePHP 0.6(编译器版本 v0.6.0),所有示例都可以直接编译运行。我们从「为什么要做」讲起,逐步带你走完环境准备、模块导入、对象操作、运算符、回调、异常处理,最后用一个 numpy 求解线性方程组的实战收尾。
一、为什么 PHP 需要 Python
先看一个很具体的场景:求解线性方程组 Ax = b。
PHP 生态里没有一个像样的线性代数库;而 Python 的 numpy.linalg.solve 一行就能搞定。如果 TypePHP 不能直接调用 Python,你只能:
- 单独维护一个 Python 服务,PHP 通过 HTTP/RPC 调用;
- 用
shell_exec 启动 Python 子进程,序列化传参、解析输出;
三条路都不理想。而有了 TypePHP 的 Python 互调用,这件事变成这样:
usepython\numpy\linalgaslinalg;usePython\numpyasnp;functionmain(): void{ $A = np\array([[3, 1], [1, 2]]); $b = np\array([9, 8]); $x = linalg\solve($A, $b);echo'解向量 x: ', $x, "\n";}
编译运行:
./tpc solve.php./solve
输出:
解向量 x: [2. 3.]
不用起子进程、不用 RPC、不用 JSON,numpy 就像 PHP 自己的扩展一样被调用。这就是 Python 互调用要解决的问题。
二、phpy:TypePHP 的 Python 运行时底座
TypePHP 并没有重新实现一个 Python 解释器。它的 Python 能力建立在 phpy 之上——phpy 是 Swoole 团队开发的一个 PHP 扩展,让 PHP 代码可以调用 Python 模块。
在此基础上,TypePHP 做了三件关键的事:
1. 不经过 ZendVM,直接走原生 C API 调用 phpy。 普通 PHP 代码调用 phpy,每一行都要在 ZendVM 里解释执行 opcode。TypePHP 把 PHP 代码 AOT 编译成原生机器码,编译产物直接通过 Zend 的 class / method / object C API 调用 phpy 提供的 PyCore、PyObject、PyList 等类,跳过了 ZendVM 的解释执行层。
2. 把 PHP 代码编译成机器指令,运算性能远超解释执行的 PHP 或 Python。 你写在 PHP 侧的逻辑——循环、计算、字符串处理、对 Python 返回结果的二次加工——都跑在编译后的原生代码上,不再是逐条解释 opcode。Python 调用本身仍由 CPython 解释执行,但「用 Python 算、用 TypePHP 组装」这个模式,已经从「只能做原型验证」进化到了「正式产品可用」。
3. 提供语法级别的 Python 互调用支持,写法更自然。 直接对比一下。用原生 phpy 调用 math.sqrt:
$module = PyCore::import('math');$result = $module->sqrt(81);echo PyCore::scalar($result), "\n";
用 TypePHP 的语法:
usepython\math;echo math\sqrt(81)->toValue()->toFloat(), "\n";
use python\math 建立模块别名,math\sqrt(81) 直接调用函数,->toValue()->toFloat() 显式转成 PHP 的 float。模块、函数、运算符都变成了语言的一部分,而不是一套需要手动管理的笨重 API。
三、环境准备:安装 phpy
Python 互调用是可选的扩展级特性。不使用 Python 语法的 TypePHP 程序不依赖 phpy;只有当你用到 use python\... 时,才需要加载它。
安装 phpy 需要 CPython、开发头文件,以及匹配的 PHP 环境。phpy 支持 Linux、macOS 和 Windows,当前要求 Python 3.10 或更高版本、PHP 8.1 或更高版本。以源码构建为例:
git clone https://github.com/swoole/phpy.gitcd phpyphpize./configure --enable-phpy --with-python-config=/usr/bin/python3-configmake -jsudo make install
在 TypePHP 程序使用的 php.ini 中加载扩展:
extension=phpy
验证扩展和 Python 运行时:
php --ri phpy/usr/bin/python3 --version
第三方 Python 包必须安装到 phpy 所使用的同一个 Python 环境中。例如后面要用到的 numpy:
/usr/bin/python3 -m pip install numpy
两个容易踩的坑:一是 phpy 与 TypePHP/PHP 的 Zend ABI 必须匹配,不要混用针对不同 PHP 版本、ZTS/NTS 模式或 Debug/Release ABI 构建的扩展;二是「编译 PHP 源码用的 php」和「运行编译产物的 php」要保持一致,否则可能在启动或对象析构时崩溃。
TypePHP 并不直接链接 libphpy.so,编译器只生成对 Zend class / method / object API 的动态调用。因此没用到 Python 的代码,不会增加任何 phpy 运行时依赖;如果程序实际执行了 Python 表达式但 phpy 未加载,会抛出普通 PHP Error。
四、第一个程序:调用 math.sqrt
先来一个最小可运行的程序,体会一下完整链路:
<?phpusePython\math;functionmain(): void{ $result = math\sqrt(81);echo $result->toValue()->toFloat(), "\n";}
编译并运行:
./tpc hello.php./hello
输出:
9
这里有几件事值得注意:
use Python\math; 对应 Python 的 import math;math\sqrt(81) 用命名空间函数语法调用 Python 的 sqrt 函数;sqrt 的返回值默认仍是一个 PyObject(Python 对象代理),不是 PHP 的 float;toValue() 把它显式转换进 TypePHP/PHP 类型系统,toFloat() 再得到 float。
TypePHP 不会根据 Python 的运行时类型偷偷改变变量类型。想要 PHP 标量,就必须显式调用 toValue() 和对应的转换方法。这个「默认保留 Python 对象、显式转换」的设计贯穿整个 Python 互调用。
顺带一提:如果只是想把它打印出来,echo 一个 PyObject 会自动调用 toString(),可以直接 echo $result;。但那样得到的是 Python 的字符串表示 "9.0";想要数值 9,就必须走 toValue()->toFloat()。关于 toString()、toValue()、toArray() 三个方法的分工,详见第九节。
五、导入 Python 模块
use python\module 建立编译期的模块别名,用法和 PHP 的命名空间 use 完全一致:
usepython\sys;usePython\numpyasnp;usepython\numpy\linalgaslinalg;
分别对应 Python 的:
import sysimport numpy as npimport numpy.linalg as linalg
大小写规则
python 这个根命名空间不区分大小写,python、Python、PYTHON 都合法;但模块路径、成员名、方法名和关键字参数严格区分大小写:
python\len([1, 2, 3]); // 正确Python\len([1, 2, 3]); // 正确python\Len([1, 2, 3]); // 错误:Python 中不存在 Len
惰性导入
use python\... 只建立编译期别名,模块在第一次实际访问时才真正导入。只声明但从未使用的模块,不会触发 Python 调用,也不会因为模块没安装而报错:
usepython\module_that_is_not_installed;functionmain(): void{echo"This program does not import the module.\n";}
这段代码可以正常编译运行,因为那个不存在的模块从未被访问。
命名空间里的坑
use 完全遵循 PHP 的命名空间与别名规则,TypePHP 不会为 Python 改写名称解析。在全局命名空间里,可以直接写 python\math\sqrt();但在 namespace App 里,python\math\sqrt() 会被 PHP 解析成 App\python\math\sqrt(),因此必须导入或使用全限定名称:
namespaceApp {usepython\math;functioncompute(): int{ $a = math\sqrt(16); // 导入后的别名 $b = \python\math\sqrt(25); // 全限定名称,无需 usereturn $a->toValue()->toInt() + $b->toValue()->toInt(); }}namespace {functionmain(): void {echo \App\compute(), "\n"; // 9 }}
此外,python 是 TypePHP 的特殊根命名空间,不能用来声明普通 PHP 命名空间;模块别名也不能与当前文件里其他 use 符号冲突。
六、调用模块函数、类与变量
Python 模块里的 callable,用 PHP 命名空间函数语法调用:
usePython\numpyasnp;$array = np\array([1, 2, 3]);$zeros = np\zeros([2, 3]);
np\array() 可能是函数、Python class,也可能是实现了 __call__ 的对象。TypePHP 不猜测成员种类,可调用性由 Python 在运行时判断。
模块变量用命名空间常量语法读取:
usePython\math;usePython\sys;$pi = math\pi; // 3.141592653589793$path = sys\path; // 模块搜索路径
注意:不要写成 math::pi 或 math::$pi——那是 PHP 的 class member 语法。Python 模块在 TypePHP 里映射为命名空间,成员读取由 Python 在运行时完成。
模块变量的值同样是 PyObject。TypePHP 只提供读取,不支持通过 namespace 语法覆盖或删除模块变量:
$path = sys\path; // 支持sys\path = $newPath; // 不支持unset(sys\path); // 不支持
确实需要修改 Python 模块状态时,可以显式调用 Python 的 setattr() / delattr()。
下面是一个综合示例,把函数、变量、内置函数和对象构造串起来:
<?phpusepython\sys;usePython\math;functionmain(): void{// 模块变量:命名空间常量语法 $pi = math\pi;echo $pi->toValue()->toFloat(), "\n"; // 3.1415926535897931// 模块函数:命名空间函数语法 $root = math\sqrt(16);echo $root->toValue()->toInt(), "\n"; // 4// sys.version_info 是一个 Python 对象,成员用 -> 访问 $version = sys\version_info;echo $version->major, '.', $version->minor, '.', $version->micro, "\n"; // 3.10.12}
七、Python 内置函数与对象构造
通过 python\name() 调用 Python builtin:
$length = python\len([1, 2, 3]); // 3$power = python\pow(2, 10); // 1024python\print('Hello from Python');
常用 Python 代理对象可以直接构造:
$list = python\list([1, 2, 3]); // PyList$dict = python\dict(['answer' => 42]); // PyDict$tuple = python\tuple([1, 2]); // PyTuple$set = python\set([1, 2]); // PySet$str = python\str(123); // PyStr$integer = python\int('42'); // PyObject$bytes = python\bytes("binary"); // PyObject
其中容器构造是 phpy Facade 的语法糖:
$a = python\list([1, 2, 3]);$b = new PyList([1, 2, 3]);
两者运行时语义完全相同。特别地,python\dict($phpArray) 按 PHP 数组的 key/value 构造 PyDict,而不是执行 CPython 的 dict(iterable)。
八、操作 Python 对象
Python 返回值用 phpy 已有的代理类表示:无法静态确定具体类型时统一为 PyObject,不会引入另一套 python\Object 类名。
属性和方法
$name = $object->name; // 属性读取$object->name = 'TypePHP'; // setattr$result = $object->greet('hello', suffix: '!'); // 调用,支持命名参数unset($object->name); // delattr
Python 方法支持命名参数,参数名严格区分大小写。用标准库的 types.SimpleNamespace 可以很直观地演示属性的读写删:
<?phpusepython\types;functionmain(): void{ $point = types\SimpleNamespace(x: 1, y: 2);echo'x = ', $point->x->toValue()->toInt(), "\n"; // 1 $point->x = 100; // 属性写echo'x = ', $point->x->toValue()->toInt(), "\n"; // 100unset($point->y); // 属性删 var_dump(isset($point->y)); // false}
下标和 isset()
$last = $list[-1]; // list/tuple 支持 Python 负索引$list[-1] = 42;unset($list[-2]);if (isset($dict['name'])) {echo $dict['name'];}
isset() 保持 PHP 语义:key/index 不存在,或者对应值为 Python None 时返回 false。除 KeyError 和 IndexError 之外的 Python 异常不会被吞掉。
一个完整示例:
<?phpfunctionmain(): void{ $dict = python\dict(['name' => 'TypePHP', 'year' => 2026]);echo $dict['name'], "\n"; // TypePHP var_dump(isset($dict['name'])); // true var_dump(isset($dict['missing'])); // false $dict['year'] = 2025; // 下标写echo $dict['year']->toValue()->toInt(), "\n"; // 2025 $list = python\list([10, 20, 30]);echo $list[-1]->toValue()->toInt(), "\n"; // 30(负索引) $total = 0;foreach ($list as $v) { // 迭代 $total += $v->toValue()->toInt(); }echo'total = ', $total, "\n"; // 60}
迭代与 callable
foreach ($pythonIterable as $index => $value) {echo $index, ': ', $value, "\n";}
通用 Python iterator 的 key 是从 0 开始的迭代序号;PyDict 则使用字典自身的 key/value。迭代期间发生的 Python 异常会作为 PyError 继续传播。
Python 返回的 callable 对象也可以直接调用:
$result = $pythonCallable($left, right: 42);$result = $pythonCallable(...$args);
不可调用的 Python 对象会抛出包含 Python TypeError 的 PyError。
九、参数转换与返回值
参数自动转换
进入 Python 函数、方法、构造器或运算符边界时,TypePHP 值自动转换为 Python 值:
| |
|---|
null | None |
bool | bool |
int | int |
float | float |
string | str |
| list |
| dict |
| list |
PyObject | |
| 可被 Python 同步调用的 callable proxy |
一个直观的示例:
<?phpfunctionmain(): void{// PHP list array -> Python list $list = python\list([1, 2, 3]);echo'list: ', $list, "\n"; // [1, 2, 3]// PHP map array -> Python dict $dict = python\dict(['a' => 1, 'b' => 2]);echo'dict: ', $dict, "\n"; // {'a': 1, 'b': 2}// 直接传 PHP 数组给 Python 函数,自动转换 $len = python\len([1, 2, 3, 4]);echo'len: ', $len->toValue()->toInt(), "\n"; // 4}
深拷贝的代价
数组传入 Python 时会递归深拷贝。递归数组、循环容器、过深嵌套和无效 UTF-8 会抛出 PyError,不会无限递归或崩溃。
因此,如果同一个数组要在循环里频繁传给 Python,建议提前转换并复用代理对象:
$pyItems = python\list($items);for ($i = 0; $i < 1000; $i++) { processor::consume($pyItems); // 只传 Python 对象引用}
如果每次都直接传 $items,每次跨越边界都会重新深拷贝一遍。二进制数据应使用 python\bytes();普通 TypePHP string 默认转换成 Python str。
三个关键方法:toString / toValue / toArray
Python 调用结果默认保持为 PyObject 或具体代理子类。即使 Python 返回 int、float、bool 或 str,TypePHP 也不会根据运行时类型自动改变变量类型。要把结果变成 PHP 侧的值,核心就是三个方法:
| | |
|---|
toString() | 取 Python 对象的字符串表示(等价 Python str(obj)) | |
toValue() | 把 Python 值显式转换进 PHP 类型系统,保留运行时类型 | int / float / bool / string / array |
toArray() | | |
toString():给人看的字符串
toString() 拿到的是 Python 视角的字符串表示,也就是 Python 里 str(obj) 的结果。它和「真正的 PHP 值」是两回事:
usepython\math;$sqrt = math\sqrt(16);echo $sqrt->toString(), "\n"; // "4.0"(Python float 的 str)echo $sqrt->toValue()->toInt(), "\n"; // 4(真正的 int)
math\sqrt(16) 在 Python 里返回 float 4.0,它的字符串表示是 "4.0";而 toValue() 之后得到的才是数值 4。
更重要的是,echo 一个 PyObject 时会自动调用 toString(),所以只是打印输出时可以直接写对象,不必显式 ->toString():
echo math\pi, "\n"; // 3.141592653589793echo python\list([1, 2, 3]), "\n"; // [1, 2, 3]echo python\dict(['a' => 1]), "\n"; // {'a': 1}
这也是为什么本文前面很多示例的 echo 后面直接跟了 PyObject。
toValue():要真正的 PHP 值
toValue() 把 Python 值显式转换进 TypePHP/PHP 类型系统,类型按 Python 的运行时类型映射:
python\int(42)->toValue(); // int(42)python\float(3.5)->toValue(); // float(3.5)python\str('hello')->toValue(); // string(5) "hello"python\bool(true)->toValue(); // bool(true)
转换后的结果是普通 TypePHP 值,后续运算不再走 Python protocol。要得到具体的 PHP 标量,在 toValue() 之后再用 TypePHP 的关键词方法:
$pyValue = python\int(42);$integer = $pyValue->toValue()->toInt();$float = $pyValue->toValue()->toFloat();$boolean = $pyValue->toValue()->toBool();$string = $pyValue->toValue()->toString();
toArray():要 PHP 数组
toArray() 专门把 Python 容器——list、set、tuple、dict 和迭代器对象——递归深拷贝成 PHP 数组:
python\list([1, 2, 3])->toArray(); // [1, 2, 3]python\dict(['a' => 1, 'b' => 2])->toArray(); // ['a' => 1, 'b' => 2]
不支持的 Python 类型返回空数组。注意迭代器会被消费,重复调用可能得到空数组。
怎么选
- 只想打印 / 展示:直接
echo $pyObj,或 $pyObj->toString() 拿字符串; - 想要数值、布尔、字符串继续参与 PHP 运算:
$pyObj->toValue() 后再 ->toInt() / ->toFloat() / ->toBool() / ->toString();
十、运算符:用 Python 的方式做运算
只要表达式的一侧静态类型是 PyObject 或其子类,TypePHP 就使用 Python 的完整运算符协议;另一侧的普通 TypePHP 值会先转换成 Python 对象:
$seven = python\int(7);$three = python\int(3);$sum = $seven + $three; // 10$product = $seven * 10; // 70$reflected = 10 + $seven; // 17(反向运算)$quotient = $seven / 2; // 3.5(Python 真除法)
支持的协议包括:
- 算术、幂和位运算:
+ - * / % ** << >> & | ^;
两个要点:
/ 是真除法,不是整除。 TypePHP 没有 Python 的 // 运算符,需要 floor division 时显式调用对应 Python 函数。
== 和 === 含义不同。== 使用 Python 的值相等协议;=== 使用 Python 的 object identity:
<?phpfunctionmain(): void{ $seven = python\int(7); $three = python\int(3);echo ($seven + $three)->toValue()->toInt(), "\n"; // 10echo ($seven * 10)->toValue()->toInt(), "\n"; // 70echo (10 + $seven)->toValue()->toInt(), "\n"; // 17echo ($seven / 2)->toValue()->toFloat(), "\n"; // 3.5 $list = python\list([1]); $alias = $list; var_dump($list === $alias); // true(同一个对象) var_dump($list === python\list([1])); // false(内容相同,身份不同)}
复合赋值使用 Python in-place protocol,并用协议返回的对象更新左值,因此对可变和不可变 Python 类型都能得到正确结果。
运算符协议最典型的受益场景就是 numpy——你可以直接对 ndarray 写 +、* 这样的表达式,让 numpy 去做向量化运算(见第十五节实战)。
十一、把 TypePHP 回调传给 Python
TypePHP 的函数、闭包和可调用对象可以作为 Python 参数,并由 Python 在当前调用链中同步回调。一个经典例子是 map:
<?phpfunctionmain(): void{ $values = python\list([1, 2, 3]); $mapped = python\map( fn (int $value): int => $value * 2, $values, ); $sum = python\sum($mapped)->toValue()->toInt();echo $sum, "\n"; // 12}
Python 传给回调的关键字参数会按名称绑定到 TypePHP callable 的参数。回调是同步的,只能在 TypePHP 主动发起的 Python 调用关系中使用;TypePHP 不会向 Python 注册可独立 import 的函数、类或 module。
十二、异常处理
Python module 不存在、成员不存在、参数错误、类型错误和用户代码异常,统一映射为 PyError:
<?phpfunctionmain(): void{try { python\len(); // 少了参数 } catch (PyError $error) {echo'message: ', $error->getMessage(), "\n";echo'type: ', $error->type->__name__, "\n"; }echo"继续执行\n";}
输出:
message: len() takes exactly one argument (0 given)type: TypeError继续执行
PyError 继承自 PHP 的 Exception,并保留 type、value、error 和 traceback 等原始 Python 异常对象(均为可选的 PyObject 属性)。普通 PHP 错误仍走 PHP 异常体系,例如未加载 phpy 时解析不到 PyCore 会抛出 Error。
跨 VM 异常不会被静默转成 null。处理异常后可以继续发起 Python 调用,phpy 会清理 CPython 的 pending error state。
十三、与 phpy 原生 API 混用
TypePHP 没有重新实现 Python VM,也没有创建第二套 Python 对象类。下面的写法可以混用:
<?phpusePython\os;functionmain(): void{// TypePHP 语法糖 $name1 = os\name;echo $name1, "\n"; // posix// 直接使用 phpy 的 API $module1 = PyCore::import('os');echo $module1->name, "\n"; // posix $list1 = new PyList([1, 2, 3]); $list2 = python\list([1, 2, 3]); var_dump($list1->toArray() === $list2->toArray()); // true}
use python\module、python\name() 和 Python 运算符都是 TypePHP 的编译期语法糖;CPython 初始化、GIL、引用计数、对象代理、转换和异常全部由 phpy 负责。TypePHP 不 include phpy 头文件,也不生成对 phpy C++ 符号的直接调用。
十四、开发辅助:IDE 提示 + Python 代码转换
TypePHP 在 tpc 命令里内置了两个开发期工具,帮助你在编辑器里写起来更顺手。
生成 IDE 自动提示
用 python\math\sqrt() 这样的语法时,编辑器默认不知道符号的位置、参数和返回类型。--gen-python-helper 会读取目标模块的运行时信息,生成一份仅供编辑器索引的 PHP 辅助文件:
./tpc --gen-python-helper math./tpc --gen-python-helper numpy.linalg./tpc --gen-python-helper numpy --output-dir .ide-helper
生成的 ide-helper/python/math.php 大致长这样:
namespacepython\math;const pi = new \PyObject();functionsqrt(mixed $x): \PyObject{ die(\PyObject::IDE_HELPER_ONLY); }functioncos(mixed $x): \PyObject{ die(\PyObject::IDE_HELPER_ONLY); }
有了它,自动补全、参数提示和跳转定义都能正常工作。注意:这些文件只能交给 IDE 索引,不要 include,也不要加入项目的 sources 或编译输入。
Python 代码转 TypePHP
--convert-python-to-php 可以把一个 Python 脚本机械地转换成使用 TypePHP Python namespace 语法的 PHP 源码,作为迁移的起点:
./tpc --convert-python-to-php script.py > script.php
比如下面这段 Python:
import mathradius = 5area = math.pi * radius ** 2print(f"area = {area}")
转换结果是:
/** @generated from script.py */usepython\math;functionmain(): void{global $radius, $area; $radius = 5; $area = math\pi * $radius ** 2;echo'area = ' . $area->toString(), "\n";}
转换结果可以继续编译运行,输出 area = 78.53981633974483。转换器遵循「不能可靠保持语义就拒绝」的原则:class、async、with、try、推导式、生成器、嵌套函数、链式比较等尚未完成的语法,会直接报错并给出源文件与行号,不会生成看似可用但语义错误的 PHP 代码。
十五、实战:用 numpy 求解线性方程组
回到开头的问题。有了上面的基础,用 numpy 做数值计算就是水到渠成的事。
先做几组向量化运算:
<?phpusePython\numpyasnp;functionmain(): void{ $a = np\array([1, 2, 3, 4]); $b = np\array([5, 6, 7, 8]); $sum = $a + $b; // 逐元素相加echo'sum: ', $sum, "\n"; $scaled = $a * 10; // 广播echo'scaled: ', $scaled, "\n"; $mean = np\mean($a); // 统计echo'mean: ', $mean->toValue()->toFloat(), "\n";echo'shape: ', $a->shape, "\n";}
输出:
sum: [ 6 8 10 12]scaled: [10 20 30 40]mean: 2.5shape: (4,)
注意 $a + $b、$a * 10 直接走了第十节的 Python 运算符协议,交给 numpy 做向量化运算,而不是 PHP 侧的逐元素循环。
再看开头的线性方程组求解,完整程序如下:
<?phpusepython\numpy\linalgaslinalg;usePython\numpyasnp;functionmain(): void{// 求解线性方程组 Ax = b// 3x + y = 9// x + 2y = 8 $A = np\array([[3, 1], [1, 2]]); $b = np\array([9, 8]); $x = linalg\solve($A, $b);echo'解向量 x: ', $x, "\n";}
./tpc solve.php./solve
输出:
解向量 x: [2. 3.]
这就是 TypePHP Python 互调用最想达到的效果:PHP 里缺失的生态,交给 Python 补上;Python 算完的结果,回到 PHP 侧继续用编译后的原生代码处理。 整个过程中没有任何子进程、RPC 或序列化。
十六、限制与边界
Python 互调用目前有几条明确的边界,提前了解能少踩坑:
- 不支持 CPython subinterpreter;
- 不把 TypePHP 编译成 Python extension,也不支持从 Python 主动 import TypePHP 程序;
- 不提供 TypePHP 函数或类的 Python 导出注解;
- 不编译 Python 源码,Python 模块仍由 CPython 在运行时加载;
- 不支持
from package import * 语法; - 不支持 Python builtin 的一级可调用语法;
- Python 互调用目前不适用于 TypePHP WASM 目标。
TypePHP 的目标是让应用高效、可靠地调用 Python 包,而不是在 PHP 中完整实现 Python 语法或异步运行时。
结语
Python 互调用,补上了 PHP 生态里最缺的一块:AI 与科学计算。
过去,PHP 程序员想用 numpy、pandas、scikit-learn 这类能力,基本只能绕道子进程或 RPC;现在,TypePHP 让这些库直接变成了「可以 import 的模块」。而且因为 TypePHP 本身是 AOT 编译器,你用 Python 算、用编译后的原生 PHP 代码组装和分发,性能不再是「只能做原型」的瓶颈。
从 math\sqrt 到 numpy.linalg.solve,本文的所有示例都是可运行的最小程序。如果你想动手验证,从「环境准备」装好 phpy 开始,把第一个 math.sqrt 程序编译跑通,再逐步尝试导入模块、操作对象、写回调、接 numpy,就能完整走一遍 Python 互调用的链路。
TypePHP 的 Python 互调用还在持续完善中——更丰富的类型推断、更顺滑的 IDE 体验、更多边界场景的打磨,都会在后续版本中到来。如果你对「让 PHP 直接用上 Python 生态」感兴趣,欢迎关注我们的进展。
技术社区
TypePHP 由识沃科技(Swoole 团队)主导研发。我们长期分享 PHP 编译技术、AOT 与 Python 互调用方向的一手实践。
欢迎添加识沃客服微信,加入技术交流群,与开发者直接交流、获取最新版本与构建指南。
识沃客服