目前市场上有一些可以将Python代码转换为PHP代码的工具,例如:
- Transcrypt:Transcrypt是一个将Python代码转换为JavaScript和PHP的工具。它可以将Python代码转换为相应的PHP代码,并保持语法和结构的一致性。
- Pythran:Pythran是一个专门用于加速Python代码执行的工具,但它也提供了将Python代码转换为C++和PHP的功能。
- PHPPy:PHPPy是一个能够将Python代码转换为PHP扩展模块的工具。它允许你使用Python编写PHP扩展,以便在PHP环境中运行。
至于将Go代码转换为PHP代码的工具,目前市场上缺乏成熟的解决方案。由于Go和PHP在语言结构和特性上有很大差异,因此直接进行自动转换可能会面临很多挑战。推荐的做法是手动重写或者根据Go代码逻辑重新编写相应的PHP代码。
无论使用哪种工具,都需要对转换后的代码进行验证和测试,确保转换后的代码能够正确运行并达到预期的功能。
Pythran:Python->C++的转换编译器
Pythran是一个Python到C++转换工具,和Cython一样,它的目的也是提高Python代码的执行效率,但是它们在实现方法上有本质的不同。
Pythran的源代码:https://github.com/serge-sans-paille/pythran
Pythran图示:
Python执行计算密集型的代码非常慢,和编译型语言C++相比,差了不止一个数量级。
本文以计算圆周率为例,看看使用Pythran后,执行效率能提升多少。
计算的Python代码(pi.py):
def pi(nsteps):
sum, step = 0.0, 1.0 / nsteps
for i in range(nsteps):
x = (i - 0.5) * step
sum += 4.0 / (1.0 + x**2)
return step * sum
print(pi(10000000)) # 步进次数越多越精确
大概用时6.8s。
计算的C++代码(pi.cpp):
#include
double pi(size_t nsteps) { // 算法和上面的Python代码一样
double sum = 0.0, step = 1.0 / nsteps;
for(size_t i = 0; i
大概用时0.13s。
使用Pythran
安装Pythran,我使用Ubuntu系统:
$ sudo apt-get install build-essential
$ sudo apt-get install libgmp-dev libblas-dev
$ sudo apt-get install python-dev python-ply python-networkx python-numpy
$ sudo pip install pythran
其它系统的安装参看Readme.。
创建要转为本地库的Python代码(fast_pi.py):
# 下面一行定义要导出的函数
#pythran export pi(int)
def pi(nsteps):
sum, step = 0.0, 1.0 / nsteps
for i in range(nsteps):
x = (i - 0.5) * step
sum += 4.0 / (1.0 + x**2)
return step * sum
直接转为本地so库:
$ pythran fast_pi.py
在Python中使用fast_pi.so(test.py):
import fast_pi
print(fast_pi.pi(10000000))
大概用时0.15s。
总结
纯Python计算用时6.8s
纯C++计算用时0.13s
使用Pythran把Python转为本地库计算用时0.15s
Pythran的转换效率还是挺高的,和纯C++相差无己。
文档:https://pythonhosted.org/pythran/
=———————————————————————
Transcrypt:剖析 Python 转 JavaScript 编译器
- Jacques de Hooge
- Rays
- 2017-04-24
- 本文字数:8318 字
阅读完需:约 27 分钟
本文要点
- 出于程服务器托管网序紧凑性、执行速度和开发速度上的考虑,在浏览器中运行的语言应该被同构地预编译为 JavaScript。
- 要实现大型 Web 应用开发中的有效合作,模块边界应符合团队的边界。
- 模块内部可以存在动态类型,但在外部应使用静态类型。
- 在客户端和服务器端使用同一技术有助于提升可扩展性。
- Python 在浏览器环境中的发展前景应该与 Python 自身的发展前景相关,而非过多依赖于特定的实现。
后端技术的特点在于可使用各种各样的编程语言实现。对于不同类型的任务,都会有适合的工具。但是在前端却是千篇一律地使用 JavaScript。只有一把锤子的人当然会将所有的问题都看成是钉子。为打破这种限制,JavaScript 的源到源编译器(Source-to-source compiler)日益增多。这类编译器可用于多种语言,例如 Scala、C++、Puby 和 Python。Transcrypt 是一个新推出的开源编译器,可以将 Python 转成 JavaScript,目标是生成大小相近的文件并以 JavaScipt 的速度执行 Python 3.6。
这类日常使用的 JavaScript Web 开发工具如果想要成为一个有吸引力的可选方案,至少需要满足以下三方面的需求:
- 从用户的角度看,使用工具创建的与原生使用 JavaScript 开发的 Web 站点和应用在观感、页面加载时间、页面启动时间和持久速度等方面上应难以区分。
- 从开发人员的角度看,他们希望借助这些工具能够无缝地访问其他 JavaScript 库,进行高效的调试。
- 从商业的角度看,应不断有大量的开发人员愿意接受该工具的专业培训,并在企业中使用,学习工具所花费的时间应能很好地转化为生产力,并且使用该工具创建的应用够满足不断变更的需求。
一个此类转化工具要取得成功,必须要达到上述三个方面的需求。各编译器正尽量在这三个方面需求间达到平衡。对于在日常生产环境中使用的编译器来说,其中任何一个方面都不能被忽略。就Transcrypt而言,这三个方面的需求都在特定的 Transcrypt 设计决策中起到了决定性作用。
需求一:
Web 站点和应用的观感与所用的底层 JavaScript 库有直接的关系。因此想要具有相同的观感,站点或应用必须正确地使用同一软件库。
虽然快速的网络连接可能会隐藏其中的差异,达到同样的页面加载时间,甚至对于在公共网络或托管主机上运行近似大小代码的移动设备也是如此。这使得在加载每个新页面时,不可能去下载一个编译器、虚拟机或较大的运行时。
只有当代码是在服务器端静态预编译成 JavaScript 时,才有可能获得与使用原生 JavaScript 同样的页面启动时间。页面中需要的代码量越大,差别就会变得愈发明显。
要获得相同的持久速度,必须生成高效的 JavaScript 代码。鉴于 JavaScript 虚拟机已针对通用的编程模式做了高度的优化,生成的 JavaScript 应该类似于手工编写的 JavaScript,而不是效仿堆栈机器或是任何其他的底层抽象。
需求二:
要实现对所有 JavaScript 库的无缝访问,Python 和 JavaScript 必须使用一致的数据格式、一致的调用模型和一致的对象模型。一致的对象模型要求 JavaScript 的基于原型的单继承机制与 Python 的基于多继承的机制融合在一起。应注意的是,JavaScript 近期添加的关键字“class”对于弥合这个根本性的差异需求完全没有影响。
要实现高效调试,必须在源代码层面完成断点设置和代码单步执行这类工作。换句话说,源代码映射是非常有必要的。一旦遇到问题,需要通过检查生成的 JavaScript 代码来找出原因。因此,所生成的 JavaScript 应该与 Python 源代码同构。
利用已有的技术意味着源代码必须是纯 Python 的,而非一些更改了句法的变体。一种稳健的实现做法是使用 Python 的原生解析器。同样,在语义上也必须是存 Python 的,该需求会造成一些实际问题,需要引入编译器指令以维持运行时的效率。
需求三:
要保护企业在客户端 Python 代码上的投入,工具需要具有持续性。持续可用的客户端 Python 编译器应具有良好的一致性和卓越的性能。如何维持这两者间的平衡是编译器设计中最关键的部分。
Python 至今已连续三年成为排名第一的计算机科学导论课程的教学语言,这一现状足以保证受过培训的 Python 开发人员持续可用。Python 已用于我们所能想到的所有后端计算领域上。如果浏览器端编程可以使用 Python 实现的话,那么所有的 Python 开发人员都可以进行浏览器端编程。这些开发人员曾经设计了长期运行的大型系统,而非孤立的、短期运行的前端脚本代码段。
就生产率而言,Python 在显著增加产出的同时保持了程序运行时的性能,这一点已得到那些从其它编程语言转到 Python 的开发人员的公认。对于那些关键运算来说,比如数值处理和 3D 图形处理,它们所使用的库已经被编译成了本地机器码,这也就是为什么 Python 能够保持运行时的性能。
最后一点,对不断发生变更的需求应具有开放性,这意味要在各个层级上支持模块化和灵活性。基于类的面向对象编程为此做出了很大贡献,它提供了多继承和复杂的包和模块机制。此外,开发人员可以通过使用命名参数和默认参数在不改变现有代码的情况下改变调用签名(call signature)。
对比一致性和性能:语言趋同发挥了作用
一些 Python 的构件与 JavaScript 构件非常近似,尤其是当转译成最新版本的 JavaScrirpt 时。两个语言间明显趋同。具体而言,越来越多的 Python 元素已经融入 JavaScript 中,例如:for...of...
、类(以有限的形式)、模块、解析赋值(destructuring assignment)和参数展开(argument spreading)。因为 JavaScript 虚拟机已经对for...of...
这类构件做了高度优化,有利于这类 Python 构件转化为最近似匹配的 JavaScript 构件。这样同构转化所生成的 JavaScript 代码能受益于目标语言的优化机制,也易于阅读和调试。
虽然 Transcrypt 中很多的调试是通过源映射(source map)在 Python 中逐步进行的,而不是在 JavaScript 代码中进行的,但是工具不应该隐匿底层的技术,而应揭示底层技术,让开发人员可以完全知道“事情的真相”。这一点更为可取,因为如果使用了编译器指令,在 Python 源代码的任何地方都可以插入原生的 JavaScipt 代码。
下面是一个使用了多继承的代码段,展示了 Python 与 Transcrpyt 转化的 JavaScript 代码之间的同构。原始的 Python 代码是:
复制代码
class C (A, B):
def __init__ (self, x, y):
A.__init__ (self, x)
B.__init__ (self, y)
def show (self, label):
A.show (self, label)
B.show (self, label)
转化后的 JavaScript 代码是:
复制代码
var C = __class__ ('C', [A, B], {
get __init__ () {return __get__ (this, function (self, x, y) {
A.__init__ (self, x);
B.__init__ (self, y);
});},
get show () {return __get__ (this, function (self, label) {
A.show (self, label);
B.show (self, label);
});}
});
侧重同构转化的局限性存在于细微之处,有时两个语言之间的差异是难以处理的。例如,Python 中可以使用“+”操作符连接列表,而如果在 JavaScript 中同构地使用“+”操作符,不仅会导致列表被转化为字符串,而且字符串会粘连在一起。当然,a + b
可以被转换为__add__ (a, b)
,,但是因为a
和b
的类型在运行时才能确定,这会导致即使对于1 + 1
这样简单的事情,也会生成函数调用和动态类型检查代码。再给出一个关于如何解释“真值(truthyness)”的例子。空列表在 JavaScript 中的布尔值是True
(或者true
),而在 Python 中则是False
。要在应用中全局地处理这个问题,需要对每个 if 语句执行一次转换,因为在 Python 构件if a:
中不能判定a
是一个布尔型,还是列表等其它类型。因此if a:
必须转换为if( __istrue__ (a))
。如果在内层循环如此使用,会再次导致性能不高。
在 Transcrypt 中,嵌入代码中的编译指令(即编译指示)用于编译本地控制这类构件。这允许了使用标准数学符号编写矩阵计算,例如M4 = (M1 + M2) * M3
,同时不会对perimeter = 2 * pi * radius
这样的语句生成任何额外的开销。从语法上说,编译指示仅是在编译时执行对__pragma__
函数的调用,而非在运行时。导入包含def __pragma__ (directive, parameters): pass
的桩模块(stub module),可允许该代码无需修改即可在 CPython 上运行。此外,编译指示可以置于注释中。
在避免命名冲突的同时统一类型系统
Transcrypt 统一了 Python 和 JavaScript 的类型系统,而非让它们毗邻而居并实时转换。数据转换需要花费一些时间,还增大了目标代码的规模以及内存的使用,进而增加了垃圾回收的负担,使得 Python 代码和 JavaScript 库间的交互难以处理。
因此,Transcrypt 的决策是去拥抱 JavaScipt 世界,而非创建一个平行的世界。下面提供了一个使用了 Plotly.js 库的简单例子:
复制代码
__pragma__ ('jskeys') # 为了方便,允许字典键值使用 JavaScript 风格的不加引号的字符串常值
import random
import math
import itertools
xValues = [2 * math.pi * step / 200 for step in range (201)]
yValuesList = [
[math.sin (xValue) + 0.5 * math.sin (xValue * 3 + 0.25 * math.sin (xValue * 5)) for xValue in xValues],
[1 if xValue
其中的编译指示语句是可选的,它允许字典键值忽略引号,只是为了方便。除此之外,代码看上去非常类似于相应的 JavaScript 代码。你可以注意一下代码中是如何使用列表解析式的,这是在 JavaScipt 中依然缺乏的特性。开发人员不用关心 Python 字典的字面量(literal)是如何映射为 JavaScript 字面量对象的,他们可以在编写 Python 代码时使用 Plotly.js 的文档。转化并非是在幕后完成的。在任何情况下,Transcrypt 字典都是一个 JavaScript 对象。
统一类型系统时会产生命名冲突。例如,Python 和 JavaScript 字符串都具有一个split()
方法,但是两者在语义上有很大不同。还存在很多类似的冲突情况,Python 和 JavaScript 仍在发展演化,未来还会有其它的冲突。
为了解决这个问题,Transcrpyt 支持别名这一概念。当在 Python 中使用.split
时,就会被翻译成一个具有 Python 的split
语义的 JavaScript 函数.py_split
。在原生 JavaScript 代码中,split
指代的是原生 JavaScript 的split
方法。可以从 Python 调用 JavaScript 的原生split
方法,这时会称其为js_split
方法。虽然在 Transcrypt 中对这一类方法预定义了可用的别名,但是开发人员可以自定义新的别名,或是取消已有别名的定义。这种方式可以解决所有统一类型系统所导致的命名冲突问题,无需付出运行时代价,因为别名是在编译时进行的。
别名也允许从 Python 标识符生成 JavaScript 标识符。例如,在 JavaScript 中允许将$
符号作为命名的一部分,而在 Python 中是不允许的。Transcrypt 严格遵循 Python 的语法,使用原生 CPython 解析器做解析,语法与 CPython 相同。一部分 JQuery 代码看上去如下:
复制代码
__pragma__ ('alias', 'S', '$')
def start ():
def changeColors ():
for div in S__divs:
S (div) .css ({
'color': 'rgb({},{},{})'.format (* [int (256 * Math.random ()) for i in range (3)]),
})
S__divs = S ('div')
changeColors ()
window.setInterval (changeColors, 500)
因为 Transcrypt 使用编译而非解释,为允许加入极简化(minification)和涉及所有模块的交付,必须在编译前确定导入的库。为此,Transcrypt 还支持 C 风格的条件编译,这可以从下面的代码片段中看到:
复制代码
__pragma__ ('ifdef', '__py3.6__')
import dashed_numbers_test # import 只用于 Python 3.6,只有在 3.6 中才支持。
__pragma__ ('endif')
在 Transcrypt 运行时中,对 JavaScript 5 和 6 的代码之间的转换使用了同一机制:
复制代码
__pragma__ ('ifdef', '__esv6__')
for (let aClass of classinfo) {
__pragma__ ('else')
for (var index = 0; index
这种方式考虑了较新版本 JavaScript 中的优化,并保持了向后兼容。在一些情况下,优化的优先级要高于同构:
复制代码
# 将 i += 1 转化为 i++,i -= 1 转化为 i--。
if type (node.value) == ast.Num and node.value.n == 1:
if type (node.op) == ast.Add:
self.emit ('++')
return
elif type (node.op) == ast.Sub:
self.emit ('--')
return
一些优化是可选的,例如是否能激活调用缓存。这会导致直接重复调用继承而来的方法,而非通过原型链(prototype chain)。
对比静态类型与动态类型:脚本语言正走向成熟
对静态类型优点的认可正在复苏,TypeScript 就是一个很好的例子。与 JavaScript 不同,静态类型语法是 Python 语言不可分割的一部分,Python 原生解析器就支持静态类型语法。但是类型检查本身却留给了第三方工具,最著名的就是 mypy。这是 Jukka Lehtosalo 的一个项目,Python 的创建者 Guido van Rossum 也是该项目的贡献者。为实现在 Transcrypt 中高效地使用 mypy,Transcrypt 团队也为项目贡献了一个轻量级 API,无需经由操作系统直接从另一个 Python 应用激活 mypy。虽然 mypy 依然在开发中,它已经可以在编译时捕获为数不少的输入错误。静态类型检查是可选的,可以在本地通过插入标准类型注解来激活。一个使用注解的例子是 mypy 的 in-porcess API:
复制代码
def run(params: List[str]) -> Tuple[str, str, int]:
sys.argv = [''] + params
old_stdout = sys.stdout
new_stdout = StringIO()
sys.stdout = new_stdout
old_stderr = sys.stderr
new_stderr = StringIO()
sys.stderr = new_stderr
try:
main(None)
exit_status = 0
except SystemExit as system_exit:
exit_status = system_exit.code
sys.stdout = old_stdout
sys.stderr = old_stderr
return new_stdout.getvalue(), new_stderr.getvalue(), exit_status
正服务器托管网如上例所示,静态类型可被用于任何适合的位置。在上面的例子中是用在 run 函数的签名中,因为它是 API 模块的一部分,可以被另一个开发人员从外部看到。如果有人错误解释了 API 的参数类型或是返回类型,mypy 将显式地给出一个错误消息,指向产生不匹配的文件和行数。
动态类型这一概念依然处于 Python 和 JavaScript 这些语言的中心位置,因为它允许灵活的数据结构,并有助于降低执行任务所需的代码量。源代码量是十分重要的,因为要理解和维护源代码,首先要通读代码。就此意义而言,实现同一功能,100KB 的 Python 源代码要优于 300KB 的 C++ 源代码,还不存在读取类型定义的困难,这些类型定义中可能会使用模块、显式类型检查和转化代码、重载的构造函数和方法、处理多态数据结构和类型依赖的抽象基类。
对于由单个编程人员编写的、源代码在 100KB 以下的小脚本,动态类型只具有优点,因为只需要非常小的规划和设计,而且编程中所有事情也会有条不紊。但是当应用增大到无法由个人构建而需要团队时,这种平衡就发生了改变。对于这样的应用,即以大约 200KB 以上源代码为特征,编译时类型检查的缺失会导致如下后果:
- 很多错误只有在运行时才能被捕获,通常是在整个过程的晚期阶段,修复这些问题需要付出高昂的代价,因为这些错误影响了更多已编写好的代码。
- 由于缺少类型信息,对模块接口可做多种解释。这意味着为了能够正确使用 API,在团队成员间所做的协商需要花费更多的开发时间。
- 尤其是在大型团队中工作时,动态类型接口会导致不必要的模块耦合。而良好定义的稀疏接口才是我们需要的东西。
即便是只有一个参数的接口,如果参数指向的是一个复杂的、动态类型的对象结构,该接口就无法保证稳定的关注分离。虽然这类“4W”(Who did What,Why and When)编程模式虽然带来了极大的灵活性,但同时也导致了设计的延后,影响到大量已有的代码。
应用“耦合与内聚”范式。模块内部可以在设计决策上具有强耦合,但是模块之间最好是松耦合的,一个更改模块内部结构的设计决策不应该影响到其它的模块。基于上述的原则,在动态类型和静态类型间做出选择时可以参考如下的经验法则:
- 对于特定的模块内部,设计决策是允许耦合的。将模块设计为内聚实体,会导致更少的源代码量,以及易于对各种实现进行实验。对此,动态类型是一种有效的方法,它可以用最小的设计时间开销换取最大的灵活性。
- 在模块间的边界上,对于要交换什么信息,开发人员应准确地制定稳定的“合约”。采用这种方法,开发人员可以并行工作,无需经常性地进行协商,他们的目标是固定的,不会发生变化。静态类型适合这些要求,对于哪些信息可以作为 API 的交互载体,它能给出正式的、经机器验证的一致意见。
因此虽然当前的静态类型浪涌看上去像是一个回归,但事实上并不是。动态类型已取得了一席之地,并不会离开。反之也成立,C#这样的传统静态类型语言也已吸收了动态类型概念。但是考虑到使用 JavaScript 和 Python 等语言编写的应用的复杂性与日俱增,有效的模块化、协作和单一验证策略愈发重要。脚本语言正走向成熟。
为什么客户端要选择 Python 而非 JavaScript
由于 Web 编程的极大普及,JavaScript 也正受到很多关注和投资。在客户端和服务器使用同一语言有其明显优点。其中的一个优点是,随着应用规模的增长,代码可以从服务器端移动到客户端。
另一个优点是概念上的一致性,这使得开发人员可以同时在前端和后端工作,无需经常在技术间做转换。Node.js 这样平台广受欢迎,正是由于人们希望降低应用客户端和服务器端在概念上的距离。但同时,这也将当前 Web 客户端编程的“放之四海皆准”风险扩展到服务器端。有人认为 JavaScript 是一种足够好的语言。近期的版本将开始支持基于类的面向对象(类似于在原型内胆上覆盖了一层装饰)、模块和命名空间这样的特性。随着 TypeScript 的引入,使用严格类型成为可能,虽然将其集成到语言标准中仍需数年时间。
即使具有这些特性,JavaScript 仍不会成为其它所有语言的终结者。对 JavaScipt 有些言过其实了(译者注:原文借用了习语“骆驼是委员会设计的马”,讽刺委员会喜欢虚张声势)。浏览器语言市场需要的是多样性,事实上所有自由市场需要的都是多样性。这意味着我们能够为手头的工作选择正确的工具,即对钉子选用锤子,对螺丝选用螺丝刀。Python 在设计上从一开始就是以清晰性、精确可读性为准则的。其价值不应被低估。
在未来很长时间内,大多数客户端编程可能仍会选择 JavaScript。但是对于那些考虑替换语言的开发人员,对持续性有影响的因素正是语言的发展动力,而非语言的具体实现。因此最重要的是使用哪种实现,而非选择哪种语言。出于此考虑,Python 无疑是一种有效的、安全的选择。Python 有很大的知名度,越来越多的浏览器在实现中考虑了 Python,同时 Python 在保持性能的同时越来越接近 CPython 的黄金标准。
虽然新的实现会替代现有的实现,但是这个过程会一直遵循一个共识,即 Python 语言应该蕴含什么。直接切换到另一种语言,要比切换到另一个 JavaScript 库或预处理器要容易得多。服务器端的格局已经成形,多种客户端 Python 实现将会继续存在,并展开公平竞争。获胜者将是语言本身。浏览器中的 Python 将会继续下去。
关于本文作者
理学硕士Jacques de Hooge是一名 C++ 和 Python 开发人员,生活在荷兰鹿特丹。从代尔夫特科技大学信息理论系毕业之后,他就创立了自己的公司——GEATEC 工程公司。公司致力于实时控制、科学计算、石油天然气勘探和医学影像。他同时也是鹿特丹应用科技大学的兼职教师,讲授 C++、Python、图像处理、人工智能、机器人、实时嵌入系统和线性代数。他当前正在在为鹿特丹 Erasmus 大学开发一种心脏病研究软件。他也是 Transcrypt 开源项目的创始人和首席设计师。
查看英文原文:Transcrypt: Anatomy of a Python to JavaScript Compiler
=====================
其他相关工具
cpython
GitHub – m3m0r7/PHPPython: run bytecode Python by PHP 日本人写的工具
php运行python代码
还有Java版哦!
AR太辛苦了,开发停止了。正在招募志愿者。PHPJava:https://github.com/memory-agape/PHPJava
===================================
GitHub – mbrostami/PHPPy: PHP project to stand alone windows executable with python============================
GitHub – filipi/phpPythonPipe: A simple way to execute python code from PHP without using temp files
===========================
python的c语言编写的解释器
https://github.com/python/cpython
https://github.com/python/cpython/archive/refs/heads/main.zip
=————————
将JavaScript代码转换为PHP代码可能会更具挑战性,因为两种语言具有不同的语法和特性。尽管如此,您仍然可以尝试使用一些工具来辅助转换。以下是一些可能有助于将JavaScript代码转换为PHP代码的工具:
1. JS2PHP:JS2PHP 是一个开源的工具,用于将JavaScript代码转换为PHP代码。它支持将JavaScript代码的一部分或全部转换为等效的PHP代码。
GitHub – endel/js2php: JavaScript (ES6) to PHP source-to-source transpiler.
演示效果
endel/js2php demo
2. JSPHPTranslator:JSPHPTranslator 是一个在线工具,可以将JavaScript代码转换为PHP代码。它提供了一个简单易用的界面,您可以将JavaScript代码粘贴到其中,并获取转换后的PHP代码。
3. 手动转换:如果您无法找到合适的工具,您可以尝试手动将JavaScript代码转换为PHP代码。这可能需要对两种语言的语法和特性有一定了解,并进行逐行转换和修改。
无论您选择使用哪种方法,都需要手动进行一些调整和修改,以确保转换后的PHP代码适应PHP语法和结构。此外,还需要注意一些特定的JavaScript功能和库可能没有等效的PHP实现,您可能需要手动重写这些部分或者寻找相应的PHP库来替代。请记住,自动工具仅能提供初始转换,您仍然需要进行手动修改和测试以确保最终结果的准确性和可靠性。
===============================================================
js2php
这是一个实验。请不要使用它。
装置
- 安装nodejs
- 全局安装js2php:
npm install -g js2php
使用
将单个JavaScript文件转换成PHP:
js2php examples/simple.js > simple.php
因为js2php
将PHP代码输出到stdout,您可以在转换后立即运行它:
js2php examples/class.js | php
JavaScript to PHP source-to-source transpiler.Online demo
This is an experiment. Please do not use it.
Installation
- Installnodejs
- Install js2php globally:
npm install -g js2php
Usage
Convert a single JavaScript file into PHP:
js2php examples/simple.js > simple.php
Sincejs2php
outputs the PHP code to stdout, you may run it right after conversion:
js2php examples/class.js | php
Features
What does it converts?
- Classes (ES6)
- Getters and Setters (ES6)
- Namespaces (ES6)
- Loops (while / for / do-while / for-of / for-in)
- Arrow functions (ES6)
- Template strings (ES6)
- Functions and closures
- Conditionals
-
出口C# -单声道C# – MS .NETDGo语言狡猾Java 语言(一种计算机语言,尤用于创建网站)Javascript – Node.jsJavascript – V8Javascript – WebKit左上臂MzScheme/球拍OCaml八度音阶Perl语言服务器端编程语言(Professional Hypertext Preprocessor的缩写)计算机编程语言R红宝石ScilabTcl/Tk
====================================
要在PHP中直接调用C++的.so(在Linux上)或.dll(在Windows上)文件,您可以使用PHP的扩展功能来实现。以下是一些常见的方法:
- 使用PHP的FFI(Foreign Function Interface)扩展:FFI扩展允许PHP直接调用C的函数和变量。您可以使用FFI扩展加载.so或.dll文件,并在PHP中调用其中定义的函数和变量。
- 创建PHP扩展:您可以使用PHP的扩展开发工具包(PHP Extension Development Kit)创建一个自定义的PHP扩展,将C++代码编译为一个.so或.dll文件,并将其与PHP一起编译和加载。这样,您就可以在PHP中直接调用C++代码。
- 使用SWIG(Simplified Wrapper and Interface Generator):SWIG是一个用于连接C++和其他语言的工具。您可以使用SWIG生成一个中间层,将C++代码封装为PHP可识别的接口和类。然后,您可以使用生成的接口和类在PHP中调用C++功能。
请注意,直接调用C++的.so或.dll文件需要对C++代码和PHP扩展开发有一定的了解。这种方法也可能涉及到一些性能和兼容性问题。因此,在实施之前,请确保您了解自己的需求,并进行充分的测试和验证。
Simplified Wrapper and Interface Generator
欢迎来到SWIGSWIG是一个软件开发工具,它将C和C++编写的程序与各种高级编程语言连接起来。SWIG用于不同类型的目标语言,包括常见的脚本语言,如Javascript、Perl、PHP、Python、Tcl和Ruby。的列表支持的语言还包括非脚本语言,如C#、D、Go语言、Java(包括Android、Lua、OCaml、Octave、Scilab和r)。还支持几种解释和编译的方案实现(Guile、MzScheme/rack)。SWIG最常用于创建高级解释或编译编程环境、用户界面,以及作为测试和原型开发C/C++软件的工具。SWIG通常用于解析C/C++接口,并生成上述目标语言调用C/C++代码所需的“粘合代码”。SWIG还可以以XML的形式导出它的解析树。SWIG是自由软件,SWIG生成的代码兼容商业和非商业项目。 |
====================================
PHP7.4中FFI的介绍(代码示例)
不言PHP7.4中FFI的介绍(代码示例)-php教程-PHP中文网
发布: 2023-04-05 14:00:02
本篇文章给大家带来的内容是关于PHP7.4中FFI的介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
FFI扩展已经通过RFC,正式成为PHP 7.4核心扩展。
什么是FFI
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP的FFI扩展就是一个让你在PHP里调用C代码的技术。
FFI的使用非常简单,只用声明和调用两步就可以,对于有C语言经验,但是不了解Zend引擎的程序员来说,这简直是打开了新世界的大门,可以快速地使用C类库进行原型试验。
(此处有图:溜了溜了,要懂C的……)
下面通过3个例子,看一下FFI是怎样使用的。
Libbloom
libbloom是一个C实现的bloom filter,比较知名的用户有Shadowsocks-libev,下面看一下怎样通过FFI在PHP里调用libbloom。
第一步,从头文件bloom.h把主要的数据结构和函数声明复制出来:
$ffi = FFI::cdef("
struct bloom
{
int entries;
double error;
int bits;
int bytes;
int hashes;
double bpe;
unsigned char * bf;
int ready;
};
int bloom_init(struct bloom * bloom, int entries, double error);
int bloom_check(struct bloom * bloom, const void * buffer, int len);
int bloom_add(struct bloom * bloom, const void * buffer, int len);
void bloom_free(struct bloom * bloom);
", "libbloom.so.1.5");
FFI目前不支持预处理器(除了FFI_LIB
和FFI_SCOPE
),所以宏定义要自己展开。
之后就可以通过$ffi
创建已声明的数据结构和调用函数:
// 创建一个bloom结构体,然后用FFI::addr取地址
// libbloom的函数都是使用bloom结构体的指针
$bloom = FFI::addr($ffi->new("struct bloom"));
// 调用libbloom的初始化函数
$ffi->bloom_init($bloom, 10000, 0.01);
// 添加数据
$ffi->bloom_add($bloom, "PHP", 3);
$ffi->bloom_add($bloom, "C", 1);
// PHP可能存在
var_dump($ffi->bloom_check($bloom, "PHP", 3)); // 1
// Laravel不存在
var_dump($ffi->bloom_check($bloom, "Laravel", 7)); // 0
// 释放
$ffi->bloom_free($bloom);
$bloom = null;
Linux Namespace
Linux命名空间是容器技术的基石之一,通过FFI可以直接调用glibc的对应系统调用封装,从而通过PHP实现容器。下面是一个让bash在一个新的命名空间里运行的例子。
首先是一些常量,可以从Linux的头文件得到:
// clone
const CLONE_NEWNS = 0x00020000; // mount namespace
const CLONE_NEWCGROUP = 0x02000000; // cgroup namespace
const CLONE_NEWUTS = 0x04000000; // utsname namespace
const CLONE_NEWIPC = 0x08000000; // ipc namespace
const CLONE_NEWUSER = 0x10000000; // user namespace
const CLONE_NEWPID = 0x20000000; // pid namespace
const CLONE_NEWNET = 0x40000000; // network namespace
// mount
const MS_NOSUID = 2;
const MS_NODEV = 4;
const MS_NOEXEC = 8;
const MS_PRIVATE = 1
接着时我们要用到的函数声明:
$cdef="
// fork进程
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);
// 挂载文件系统
int mount(const char *source, const char *target, const char *filesystemtype,
unsigned long mountflags, const void *data);
// 设置gid
int setgid(int gid);
// 设置uid
int setuid(int uid);
// 设置hostname
int sethostname(char *name, unsigned int len);
";
$libc = FFI::cdef($cdef, "libc.so.6");
定义我们的子进程:
// 生成一个容器ID
$containerId = sha1(random_bytes(8));
// 定义子进程
$childfn = function() use ($libc, $containerId) {
usleep(1000); // wait for uid/gid map
$libc->mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, null);
$libc->setuid(0);
$libc->setgid(0);
$libc->sethostname($containerId, strlen($containerId));
pcntl_exec("/bin/sh");
};
在子进程里,我们重新挂载了/proc
,设置了uid、gid和hostname,然后启动/bin/sh
。
父进程通过clone函数,创建子进程:
// 分配子进程的栈
$child_stack = FFI::new("char[1024 * 4]");
$child_stack = FFI::cast('void *', FFI::addr($child_stack)) - 1024 * 4;
// fork子进程
$pid = $libc->clone($childfn, $child_stack, CLONE_NEWUSER
| CLONE_NEWNS
| CLONE_NEWPID
| CLONE_NEWUTS
| CLONE_NEWIPC
| CLONE_NEWNET
| CLONE_NEWCGROUP
| SIGCHLD, null);
// 设置UID、GID映射,把容器内的root映射到当前用户
$uid = getmyuid();
$gid = getmyuid();
file_put_contents("/proc/$pid/uid_map", "0 $uid 1");
file_put_contents("/proc/$pid/setgroups", "deny");
file_put_contents("/proc/$pid/gid_map", "0 $gid 1");
// 等待子进程
pcntl_wait($pid);
glibc的clone函数是clone系统调用的封装,它需要一个函数指针作为子进程/线程的执行体,我们可以直接把PHP的闭包和匿名函数当作函数指针使用。
运行效果:
$ php container.php
sh-5.0# id # 在容器内是root
uid=0(root) gid=0(root) groups=0(root),65534(nobody)
sh-5.0# ps aux # 独立的PID进程空间
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.1 10524 4124 pts/1 S 10:19 0:00 /bin/sh
root 3 0.0 0.0 15864 3076 pts/1 R+ 10:19 0:00 ps aux
sh-5.0# ip a # 独立的网络命名空间
1: lo: mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
raylib
raylib是个特性丰富而且易用的游戏库,经过简单的封装就可以在PHP里使用。下面这个例子实现了一个跟随鼠标的圆:
<?php include __DIR__ . "/../../RayLib.php";
// 初始化
RayLib::init(); // 初始化FFI和“常量”
RayLib::InitWindow(400, 300, "raylib example");
// 状态:球的位置
$ballPosition = RayLib::Vector2(-100.0, 100.0);
// 主循环
while (!RayLib::WindowShouldClose())
{
// 状态更新
$ballPosition = RayLib::GetMousePosition(); // 获取鼠标位置
// 渲染
RayLib::BeginDrawing();
RayLib::ClearBackground(RayLib::$RAYWHITE); // 清除背景颜色
RayLib::DrawCircleV($ballPosition, 40, RayLib::$RED); // 画个圈圈
RayLib::DrawFPS(10, 10); // 显示FPS
RayLib::EndDrawing();
}
// 释放
RayLib::CloseWindow();
不足
- 性能
C类库性能可能很高,但是FFI调用的消耗也非常大,通过FFI访问数据要比PHP访问对象和数组慢两倍,所以用FFI不一定能提高性能,RFC里给出的一个测试结果:
就算用了JIT,还是比不上不用JIT的PHP。
- 功能
目前(20190301)FFI扩展还没实现的一些功能:
- 返回struct/union和数组
- 嵌套的struct(我写了个简单的补丁)
使用这些功能的时候,会抛出异常,提示功能未实现,所以只用等等或者马上贡献代码就好:)
参考
- PHP RFC: FFI – Foreign Function Interface:RFC文档,有比较完整的API和设计目的
- 专栏
- Oraoto的日常
=======================
SWIG 3 中文手册——8. 预处理
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net