通过 WebAssembly 在浏览器运行 PHP

file

演示地址:PIB: PHP in Browser,你可以在上面运行 PHP 代码,然后通过复制地址栏分享代码。

项目地址:oraoto/pib

某天晚上,在看 Emscripten 的文档,发现 Emscripten 有 emconfigureemmake,可以直接用 Emscripten 替换编译器实现项目移植,灵光一现就有了这个项目。

性能测试#

Firefox 和 Edge 加载更快,用户体验要比 Chrome 好一点 :blush:

首先娱乐测试一下,改自 eechen 的测试,10 万的数组填充和字符串拼接:

<?php

$start = microtime(true);

$arr = [];

for ($i = 0; $i < 100000; $i++) {
    $time = microtime(true);
    $arr[$i . '_' . $time] = $time;
}

echo (microtime(true) - $start) . PHP_EOL;

点我直接跑一下

我的电脑 CPU 是 i5-6400,Chrome 66 耗时 0.35 秒,Firefox 耗时 0.25 秒,而原生 PHP 7.2 只需 0.048 秒,也就是说性能大约是原生 PHP 7.2 的 1/7 左右。

跑 PHP 源代码自带的 Zend/bench.php

simple             0.288
simplecall         0.088
simpleucall        0.226
simpleudcall       0.241
mandel             1.138
mandel2            1.251
ackermann(7)       0.221
ary(50000)         0.037
ary2(50000)        0.033
ary3(2000)         0.626
fibo(30)           0.855
hash1(50000)       0.067
hash2(500)         0.084
heapsort(20000)    0.264
matrix(20)         0.285
nestedloop(12)     0.444
sieve(30)          0.178
strcat(200000)     0.043
------------------------
Total              6.369

而原生 PHP 7.2 只要 0.591 秒,差了近 11 倍。

功能测试#

因为是直接编译 PHP 解析器,所以语言层面的大部分功能都是支持的,目前已知不支持的只有 Generator(已支持)。

可以试试 PHP7 的新特性:

库函数方面支持比较少,默认只编译了 datepcrebcmathctypejsonReflectionSPLtokenizerstandardCore 这些扩展。

实现原理#

原理并不复杂,就是用 Emscripten 把 PHP 解释器编译到 WebAssembly,然后通过 JavaScript 调用 Zend 的 API。

为了能让 PHP 解释器编译成功,需要对代码做少量修改,主要是文件系统相关的两处代码,我只直接注释掉或者 return 跳过代码。

对比现有方案#

3v4l 这种在服务端执行代码然后返回结果到前端的方案已经很成熟,在运行和分享 PHP 代码方面,PIB 的优势就是省去了我部署服务器的钱(文件都在 Github pages)。

也有其他的在浏览器直接运行 PHP 的方案:

  • php2wasm:直接把 PHP 代码编译成 wasm,现在还不成熟
  • pyhp.js:用 Pyton 实现 PHP 解释器(PyHP,据作者说性能比 PHP7 好),然后再把这个解释器编译到 JS,支持的特性有限,作者已经弃坑

而 PIB 已经支持了大部分 PHP 语言特性,不过性能和稳定性仍需提高。

未来#

一开始设想是用 PHP 进行前端开发的,但是实现不容易,所以先做成这个样子了。

如果要让 PHP 代码操作浏览器的 DOM,必须写 PHP 扩展,使用 Emscripten 的 API 去调用 JavaScript,这还是可以做的。

而 JavaScript 很多接口都是需要回调的,Emscripten 也是可以做到,但是只是回调到 C/C++,如果要回调到 PHP,就要自己实现协程方案,这我还做不了。

目前可以完善和尝试的:

  1. 语法检查
  2. 错误信息显示
  3. 减少代码体积
  4. 处理内存泄漏

如果你有什么有趣的想法,也不妨提个 issue 或者评论一下。

2018.10.20:

  1. PHP 升级到 7.3 RC2
  2. Ace 编辑器提供基本的语法检查
  3. 通过 LLVM LTO 性能提高了 1 倍,文件缩小到 3.9M(gzip 后 1.1M)
  4. 内存泄漏再次出现(曾经的变通方案无效了)
本作品采用《CC 协议》,转载必须注明作者和本文链接
Oraoto
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 15

体验很差,打开页面后 Google Chrome Helper 进程 % CPU 直接飙升 200.. 然后忍了十秒还在加载中。不知道是不是我打开方式有问题。科学上网和正常网络都试过了,一样的结果。

6年前 评论
Oraoto

@skyjerry 目前是这样的 :joy: 因为加载 wasm 之后浏览器还要进行编译,CPU 就会爆高,Chrome 尤其明显,Firefox 和 Edge 加载会快很多。

6年前 评论

好像还是有问题,不知道是不是我本地的问题,我这次用的 Firefox,编译完 CPU 降下去了,然后.. 就没有然后了,一直加载中

6年前 评论
Oraoto

@skyjerry 这就尴尬了 §:з)))」∠) Firefox 版本是?我只试过 FF 61,不嫌麻烦的话,可以看看控制台的输出。

6年前 评论
Oraoto

@skyjerry 加载中是浏览器在转圈,还是红色按钮还是显示 Loading?如果按钮显示 Run 了就可以了。

6年前 评论

@Oraoto 浏览器转圈,不知道原因

6年前 评论

这个功能不错哦

6年前 评论
wkan

运行 exit(1) 然后就凉了

6年前 评论
Oraoto

@wkan 的确,刚刚修复了:blush: exit (1) 示例,还是有输出缓冲和内存问题。

6年前 评论

有意思的项目 👍

6年前 评论

把 php 拿到前台编译运行的,正常的流程应该是,先编译成 wasm, 然后 js fetch, 直接用,这个太慢了

6年前 评论
Oraoto

@mafa1993 文章中也提到类似的方案,难度更大,还没完整地支持 PHP 语法。现在加载和运行的确慢,正考虑精简不需要的函数来减小体积。

6年前 评论

:thumbsup: 好创意 支持

3年前 评论