Loading... # [phpy :PHP 与 Python 互调用库,为 PHP 引入 Python 生态,PHP 也可以写 AI 了](https://zhuanlan.zhihu.com/p/670373512) phpy 是识沃团队最新推出的开源项目,目标是为 `PHP` 引入 `Python` 生态,来弥补 `PHP` 生态的空缺和不足。`phpy` 使得 `PHP` 可以调用所有 `Python` 的包。 包括当下非常流行的 `PyTorch`、`transformers`、`TensorFlow` 等 `AI` 库,以及 `Numpy`、`Pandas`、`Scikit` 等科学计算库,还可以使用 `PyQt`、`wxPython` 等图形界面库。 * GitHub 地址:[https://github.com/swoole/phpy](https://link.zhihu.com/?target=https%3A//link.segmentfault.com/%3Fenc%3D7rdRaP7JOlFKe7quatP6jw%253D%253D.6OehhcFnN9z4IkDIIaY6pOMhBiIrA4NJBxw3yyZASoM%253D) > 不建议在 `php-fpm/apache` 短生命周期运行环境下使用,频繁地导入/销毁模块的开销会消耗大量资源 编译安装 ---- `phpy` 可以作为 `PHP` 的扩展,也可以作为 `Python` 的 `C` 模块。既可以在 `PHP` 代码中调用 `Python` 的库,也可以在 `Python` 中调用 `PHP` 的类和函数。 > 作为 `Python` 模块时依赖 `PHP` 的 `embed SAPI` ,检查 `PHP` 的目录中,确保存在 `libphp.so` ``` ll /opt/php-8.1/lib/libphp.so -rwxr-xr-x 1 htf htf 39397224 11月 30 19:25 /opt/php-8.1/lib/libphp.so* ``` ### 编译依赖 1. `Python 3.10` 或以上版本,建议使用 `conda` 工具来安装 2. `PHP 8.1` 或以上版本 `Python` 将安装到 `/opt/anaconda3` 目录下 * `/opt/anaconda3/bin/python``Python` 主程序 * `/opt/anaconda3/include/python3.11` 头文件 * `/opt/anaconda3/lib/python3.11` 动态链接库目录 另外需要配置 `/etc/ld.so.conf.d/conda.conf` 加入 `/opt/anaconda3/lib` 和 `/opt/php-8.1/lib` 。执行 `ldconfig` 检查是否可以找到 `libpython3.11.so` 和 `libphp.so`。 ``` sudo ldconfig -p |grep php libphp7.so (libc6,x86-64) => /opt/php-7.4/lib/libphp7.so libphp.so (libc6,x86-64) => /opt/php-8.0/lib/libphp.so sudo ldconfig -p |grep python libsamba-policy.cpython-38-x86-64-linux-gnu.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libsamba-policy.cpython-38-x86-64-linux-gnu.so.0 libpython3.11.so.1.0 (libc6,x86-64) => /opt/anaconda3/lib/libpython3.11.so.1.0 libpython3.11.so (libc6,x86-64) => /opt/anaconda3/lib/libpython3.11.so libpython3.8.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.8.so.1.0 libpython3.8.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.8.so libpython3.5m.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0 libpython3.so (libc6,x86-64) => /opt/anaconda3/lib/libpython3.so libpython2.7.so.1.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 libpython2.7.so (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libpython2.7.so ``` ### 作为 `PHP` 扩展 检查 `config.m4` 中 `Python` 路径是否正确。若 `Python` 的安装路径不是 `/opt/anaconda3`,需修改为正确的安装路径。 ``` cd phpy phpize ./configure make install ``` 安装成功后,修改 `php.ini` ,加入 `extension=phpy.so`,执行 `php -m` 和 `php --ri phpy` 检查是否成功加载扩展。 ### 作为 `Python` 模块 ``` cmake . make -j ``` 执行成功后,会生成 `tests/lib/phpy.so` 文件。可以在 `Python` 中直接导入此模块。 ``` import phpy ``` 使用方法 ---- ### 导入 Python 模块 ``` $os = PyCore::import('os'); ``` ### 执行函数 ``` $uname = $os->uname(); ``` ### 读取属性 ``` echo $uname->sysname; ``` ### 加载路径 可使用 `PyCore::import('sys')->path->append()` 将一些目录加入到加载路径列表中。 例如:`/workspace/app/user.py` 自定义的包,可以通过下面的步骤实现加载: 1. `PyCore::import('sys')->path->append('/workspace')` 将 `/workspace` 添加到 `sys.path` 中 2. `PyCore::import('app.user')` 将自动搜索 `sys.path` 找到对应的 `app/user.py` 包并载入 ### 内置方法 * `PyCore::str()` 将对象转为字符串 * `PyCore::repr()` * `PyCore::type()` 获取对象的类型 * `PyCore::locals()` 获取当前空间内容的所有局部变量 * `PyCore::globals()` 获取所有全局变量 * `PyCore::hash()` 获取 Hash 值 * `PyCore::hasattr()` 检测对象是否存在某个属性 * `PyCore::id()` 获取对象的内部编号 * `PyCore::len()` 获取长度 * `PyCore::dir()` 获取对象所有的属性、方法 * `PyCore::int()` 构造一个整数 * `PyCore::float()` 构造一个浮点数 * `PyCore::fn()` 构造一个可调用函数 * `PyCore::scalar()` 将 `PyObject` 对象转为 `PHP` 的标量类型,例如 `PyStr` 将转为 `PHP 字符串`,`Dict/Tuple/Set/List` 将转为 `Array` ### 内置类 * `PyObject`:所有其他类型的基类 * `PyDict`:字典类型,等同于 `PHP` 的关联数组 * `PyList`:列表类型,等同于 `PHP` 的索引数组 * `PyTuple`:元组,不可变的列表 * `PyStr`:字符串 * `PyModule`:`Python` 包,`PyModule` 也是 `PyObject` 的子类 `PyObject` 是除了 `PyCore` 之外,所有其他类型的基类。非内置类的对象是 `PyObject` 的实例。`PyObject` 实现了 `4` 个魔术方法,用于将操作映射到 `Python` 对象。 所有类方法、参数、返回值参考 `stubs` 目录中的文件。 ### 继承关系 ``` PyObject -> PyModule -> PySequenece -> PyList -> PyTuple -> PySet -> PyStr -> PyDict -> PyType ``` ### 整数 `Python` 语言是天然支持无限精度整型计算的,可以使用 `Python` 的整数计算能力来代替 `ext-bcmath` ### 构造 使用 `PyCore::int()` 函数来构造一个数字,可以传入整数、浮点数、字符串来初始化。 ``` $i1 = PyCore::int(12345678);$i2 = PyCore::int('1234567890123456789012345678901234567890');$i3 = PyCore::int(12345678.03); ``` ### 运算 整数同样也是 `PyObject` 的实例,可以使用内置的方法类实现运算。 ``` $i = PyCore::int(12345435);var_dump(strval($i->__pow__(3)));var_dump(strval($i->__add__(4))); ``` 将输出 `1881564851360655187875` ,由于超过了 `64位` 最大精度,因此输出结果将自动转为字符串类型。 ### 命名参数 `phpy` 支持了命名参数,可以使用命名参数来调用 `Python` 的函数和方法。 > 顺序参数必须在前,命名参数必须在最后 ``` kwargs($a, $b, $c, name: 'hello', world: 'rango'); ``` 对应的 `Python` 代码为: ``` kwargs(a, b, c, name: 'hello', world: 'rango') ``` ### 回调函数 可将 `PHP` 的可调用对象作为 `Python` 的回调函数。使用 `PyCore::fn(callable $fn)` 包裹即可。 ``` $m = PyCore::import('app.user');$uuid = uniqid();$rs = $m->test_callback(PyCore::fn(function ($namespace) use ($uuid) { var_dump($namespace); return $uuid;})); ``` * `import app.user` 导入了一个自定义 `Python` 包 * 调用了包中的一个函数 `test_callback`,此函数接受一个参数为 `Python Callable` 对象 * 使用 `PyCore::fn()` 包裹了一个 `Closure` 闭包对象作为回调,这里也支持函数名称字符串、对象方法的调用方式 * 回调函数返回了一个字符串,在 `test_callback` 函数中会得到一个 `str` 类型返回值 可参考下方的 `Python tkinter` 例子。 实际案例 ---- ### 基于 `tkinter` 实现 `GUI` 的例子 ``` <?php$tkinter = PyCore::import('tkinter');$root = $tkinter->Tk();$root->title('我的窗口');$root->geometry("500x500");$root->resizable(False, False);$button = $tkinter->Button($root, text: "Click Me!!", command: PyCore::fn(function () { var_dump(func_get_args()); echo 'click me!!' . PHP_EOL;}));$button->pack();$tkinter->mainloop(); ``` ![](https://pic1.zhimg.com/80/v2-498d3eef9a25750f9b40adb32704c254_720w.webp) ### 一个基于 `transformers` 的情感分析模型推理实现 ``` <?php$transformers = PyCore::import('transformers');$os = PyCore::import('os');$os->environ->__setitem__('https_proxy', getenv('https_proxy'));$distilled_student_sentiment_classifier = $transformers->pipeline( model: "lxyuan/distilbert-base-multilingual-cased-sentiments-student", top_k: null,);$rs = $distilled_student_sentiment_classifier ("I love this movie and i would watch it again and again!");var_dump(PyCore::scalar($rs)); ``` ![](https://pic3.zhimg.com/80/v2-21b58e8af0bcee2c6497e1fd9375ed3e_720w.webp) 最后修改:2023 年 12 月 18 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏