美文网首页PHP
利用PHP扩展 phpy 调用 Python SO

利用PHP扩展 phpy 调用 Python SO

作者: ___n | 来源:发表于2023-12-20 14:40 被阅读0次

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

    包括当下非常流行的 PyTorch、transformers、TensorFlow 等 AI 库,以及 Numpy、Pandas、Scikit 等科学计算库,还可以使用 PyQt、wxPython 等图形界面库。

    GitHub 地址:https://github.com/swoole/phpy

    注意不要在 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*
    

    编译依赖
    Python 3.10 或以上版本,建议使用 conda 工具来安装

    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。

    udo 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 -mphp --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();
    

    一个基于 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));
    

    相关文章

      网友评论

        本文标题:利用PHP扩展 phpy 调用 Python SO

        本文链接:https://www.haomeiwen.com/subject/efntndtx.html