7

phpy :PHP 与 Python 互调用库,为 PHP 引入 Python 生态,PHP 也可以写 AI 了

 9 months ago
source link: https://blog.p2hp.com/archives/11720
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

phpy :PHP 与 Python 互调用库,为 PHP 引入 Python 生态,PHP 也可以写 AI 了

phpy 是识沃团队最新推出的开源项目,目标是为 PHP 引入 Python 生态,来弥补 PHP 生态的空缺和不足。phpy 使得 PHP 可以调用所有 Python 的包。

包括当下非常流行的 PyTorchtransformersTensorFlow 等 AI 库,以及 NumpyPandasScikit 等科学计算库,还可以使用 PyQtwxPython 等图形界面库。

不建议在 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/pythonPython 主程序
  • /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:字符串
  • PyModulePython 包,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();
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));
v2-21b58e8af0bcee2c6497e1fd9375ed3e_720w.webp




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK