PPython:PHP 拥抱 Python 的利器 | Linux 中国

论坛 期权论坛 期权     
Linux中国   2019-7-7 23:42   2145   0
PPython,这是一种从根本上将 PHP 与 Python 有效结合的技术。-- 望星星降
作者:望星星降介绍
Python 与 PHP 都是广泛使用的语言,各有所长,让人期待两者结合可以实现更丰富的效果。
在 PHP 中调用 Python 实现某些处理,这种需求虽然比较小众,还是实用的。目前网上可以查到很多资料仍在探讨
  1. exec()
复制代码
(也包括
  1. system()
复制代码
  1. shell_exec()
复制代码
  1. passthru()
复制代码
等)执行外部的 Python 文件,但这只是一种通用的方式,调用成本比较高,在每次调用时,需要装载整个 Python 解释环境。
有此类需求的开发者非常适合看一下 PPython,这是一种从根本上将 PHP 与 Python 有效结合的技术。
PPython 最初见于 https://code.google.com/p/ppython,该作者将 lajp(一种 PHP 结合 Java 的技术)移植到了 Python 上。
该项目最初建立于 2012 年,而且似乎已经停止维护多年,不过目前来看其思路及效果还是值得肯定的,因此将此项目从停止运营的 Google Code 上迁移到了 GitHub,并遵循原 Apache 许可证重新发布和维护。
日前笔者对此作了一番尝试,对 PPython 的方便易用有所体会。
原理与架构
PHP 与 Python 通讯有两种不同的套接字机制:TCP 套接字和 UNIX 套接字。UNIX 套接字是 Unix/Linux 本地套接字,相对于 TCP 套接字,具有以下特点:
只能在同一台主机中通讯(IPC),不能跨主机; 传输速度大于 TCP 套接字; 服务端只向本机提供服务(没有对外侦听端口),相对安全,易于管理。PHP 和 Python 各有其语言内部定义的数据类型,通常 PHP 进程与 Python 进程进行数据交互时,需要进行转码处理。此类转换如由应用自行实现,从开发效率到运行性能都会增加不少额外负担。
PPython 对 PHP 和 Python 间的通讯方式的处理支持 TCP 套接字和 UNIX 套接字两种机制,兼顾通讯效率和分布式,转码由服务统一处理,Python 为 PHP 的数据类型提供格式兼容,使 PHP 端开发无须为底层通讯担心。
Python 因其语言 GIL 特性,同一进程内多线程效率不高。PPython 可根据项目需要部署服务,多进程运行 Python,提高应用综合性能。


使用方法
PPython 的代码可从上述项目仓库中下载。
下载得到的文件中,以下三个是 PPython 的核心代码,作用如下:
  1. php_python.py
复制代码
,Python 进程主文件,完成 Python 端监听请求并运行返回
  1. process.py
复制代码
,Python 端核心类,实现 Python 内部进程调用及 PHP 与 Python 数据结构转化等关键处理
  1. php_python.php
复制代码
,PPython 客户端,PHP 端引用此文件,可直接使用 PPython 函数实现调用。将以上文件放置到任意目录。先修改准备运行 PPython 的端口,监听端口不限,只要
  1. php_python.py
复制代码
  1. php_python.php
复制代码
两端修改一致。笔者统一改为 10240。
在当前目录下运行
  1. php_python.py
复制代码
,只要 Python 环境正常,便将运行起一个 PPython 的服务。
    1. -------------------------------------------
    复制代码
    1. - PPython Service
    复制代码
    1. - Time: 2019-05-13 22:24:09
    复制代码
    1. -------------------------------------------
    复制代码
    1. Listen port: 10240
    复制代码
    1. charset: utf-8
    复制代码
    1. Server startup...
    复制代码
PHP 端引入
  1. php_python.php
复制代码
,就可以用
  1. ppython
复制代码
函数与之前启动的 PPython 服务通讯,传入请求由 PPython 服务调用 Python 处理后返回结果,如
  1. $res = ppython('test::go')
复制代码
是调用
  1. test.py
复制代码
中的
  1. go
复制代码
函数,也可加上更多参数,第二个参数起将为被调的函数传递更多参数。
  1. php_python.py
复制代码
是 PPython 启动后直接运行的全局代码,有全局配置或进程启动后的通用处理都写在这里,如原生代码中建立了数据库连接等,项目中应视情况作优化。
但 Python 令人感兴趣的主要方面不只是像 PHP 那样描述业务功能,它可以在人工智能等领域所需要的计算型任务提供对更复杂的数据结构的处理,因此二者的结合可以给 PHP 带来更多应用场景。
改进
此外,原生的
  1. php_python.py
复制代码
还有些不足。笔者用
  1. ppython
复制代码
调用自定义代码中遇到了三个问题,并相应做了解决:
1. 不支持
  1. complex
复制代码
(复数类),复数是数学上的一种数据类型,主要包括
  1. real
复制代码
(实部)和
  1. imag
复制代码
(虚部)数据,虽然日常生活中遇到较少,但 AI 和各种专业研究领域或并不罕见。Python 里有
  1. complex
复制代码
类,对复数可以直接进行各种计算,但
  1. PPython
复制代码
序列化和反序列化对
  1. complex
复制代码
没有处理。为了能让
  1. complex
复制代码
包括的数据能正常返回,只要在
  1. process.py
复制代码
  1. z_encode()
复制代码
方法中加上符合 PHP 要求的序列化处理,代码如下:
    1. elif isinstance(p, numpy.complex128):
    复制代码
    1.     t1 = str(p.real)
    复制代码
    1.     t2 = str(p.imag)
    复制代码
    1.     return 'O:7:"complex":2:{s:4:"real";d:%s;s:4:"imag";d:%s;}' % (t1,t2)
    复制代码
2. 不支持
  1. ndarray
复制代码
(多维数组)。相比
  1. complex
复制代码
  1. ndarray
复制代码
要普通得多,相信凡使用到 Python 的各种计算功能,
  1. ndarray
复制代码
是无法回避的,甚至
  1. ndarray
复制代码
在一定程度上成就了 Python。但原
  1. php_python.py
复制代码
不能识别
  1. ndarray
复制代码
。不过解决起来并不难,在
  1. process.py
复制代码
里找到
  1. z_encode()
复制代码
方法,加上下面这段,可以直接将
  1. ndarray
复制代码
转化为符合 PHP 要求的数组(数字索引)。
    1.     elif isinstance(p, numpy.ndarray):
    复制代码
    1.         s = ''
    复制代码
    1.         i = 0
    复制代码
    1.         for d in p:
    复制代码
    1.             s += 'i:%d;%s' % (i,z_encode(d))
    复制代码
    1.             i += 1
    复制代码
    1.         return "a:%d:{%s}" % (len(p),s)
    复制代码
3. 原代码不太稳健,如数据为
  1. ndarray
复制代码
类型,
  1. if p == None:
复制代码
报错
  1. ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
复制代码
,因为
  1. p == None
复制代码
的结果也是
  1. ndarray
复制代码
,不返回
  1. false
复制代码
,将判断方法改为
  1. if p is None:
复制代码
可避免出错。相应地 PHP 端也要注意一下序列化和反序列化的处理。
处理回复中类似
  1. complex
复制代码
这样的对象数据时,如系统中没有定义相应的类,PHP 是可以反序列化的,但将显示为 “incomplete object”,
  1. vardump
复制代码
看得到
  1. real
复制代码
  1. imag
复制代码
数据,但不能直接操作,自行定义
  1. complex
复制代码
类后,则按指定的类进行解析,与 PHP 内的一般对象无异,可以轻松进行所有操作。
至此,PHP 与 Python 的功能调讯已无问题。
补充:注册为服务
命令行下启动
  1. php_python.py
复制代码
主要是方便调试,可以看到观察反馈信息等,生产环境中手工启动 PPython 毕竟不太方便。可以将 PPython 配置成服务,修改端口也可以为不同的应用配置不同的 PPython 端。
Linux 下将一个进程注册为服务很简单,只要建立
  1. /usr/lib/systemd/system/ppython.service
复制代码
,内容如下:
    1. [Unit]
    复制代码
    1. Description=PHP-Python Service
    复制代码
    1. After=network.target remote-fs.target nss-lookup.target
    复制代码
    复制代码
    1. [Service]
    复制代码
    1. ExecStart={PPYTHON_PATH}/php_python.py
    复制代码
    1. [/code][*][code][Install]
    复制代码
    1. WantedBy=multi-user.target
    复制代码
其中
  1. {PPYTHON_PATH}
复制代码
要改成实际路径。
总结
有了 PPython,可以摒弃
  1. exec()
复制代码
这种 shell 调用,使开发回归到逻辑本身。
个人认为该方案值得所有对 PHP 和 Python 都感兴趣的开发人员了解,也欢迎大家参与和贡献这个项目。
[url=][/url]
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:126
帖子:8
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP