WebAssembly 与 PHP 简介
WebAssembly (Wasm) 是一种低级的二进制指令格式,在虚拟机中执行,性能接近原生。在 PHP 环境中,它开辟了新的可能性:您可以直接从 PHP 脚本中运行 C、C++、Rust 或 Go 编写的代码,而无需离开服务器环境。这对于资源密集型任务尤其有用——如图像处理、加密、数据解析或数学计算。
在本文中,我们将介绍完整的工作流程:从将源代码编译为 Wasm,到使用 wasmtime 和 wasmer 扩展从 PHP 调用函数。您将了解如何利用机器代码的强大功能来提高 PHP 应用程序的性能。
WebAssembly 基础:PHP 开发者需要了解的内容
Wasm 架构
WebAssembly 在隔离的沙箱中运行。Wasm 模块无法直接访问主机的文件系统、网络或内存——只能访问其线性内存和导出的函数。这确保了安全性,但需要显式地管理数据。
关键概念:
- 模块 — 编译后的二进制文件 (.wasm)
- 导出 — 可从外部访问的函数和变量
- 导入 — 主机 (PHP) 提供给模块的函数
- 线性内存 — 模块操作的连续字节数组
在 PHP 中使用 Wasm 的工具
有两个主要的扩展:
- Wasmtime — 来自 Bytecode Alliance,最流行且稳定
- Wasmer — 替代方案,支持多种后端 (Cranelift, LLVM, Singlepass)
通过 Composer 安装:
composer require wasmtime/wasmtime-php# 或composer require wasmer/wasmer还需要安装 Wasmtime 或 Wasmer 系统库。对于 Ubuntu:
curl https://wasmtime.dev/install.sh -sSf | bash# 或curl https://get.wasmer.io -sSfL | sh将 C/C++ 编译为 WebAssembly
使用 Emscripten 进行编译——这是一个基于 LLVM/Clang 的工具集。安装:
git clone https://github.com/emscripten-core/emsdk.gitcd emsdk./emsdk install latest./emsdk activate latestsource ./emsdk_env.sh示例:C 语言求和函数
创建文件 math.c:
#include <emscripten.h>
EMSCRIPTEN_KEEPALIVEint add(int a, int b) { return a + b;}
EMSCRIPTEN_KEEPALIVEint factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}编译为 Wasm:
emcc math.c -o math.wasm -s WASM=1 -s EXPORTED_FUNCTIONS='["_add","_factorial"]' -s EXPORTED_RUNTIME_METHODS='["ccall","cwrap"]' -O3标志 -O3 启用最大优化。文件 math.wasm 已准备好使用。
将 Wasm 与 PHP 集成:分步示例
场景 1:使用 Wasmtime
安装扩展并编写以下 PHP 代码:
<?phprequire 'vendor/autoload.php';
use Wasmtime\\Engine;use Wasmtime\\Module;use Wasmtime\\Store;use Wasmtime\\Func;use Wasmtime\\Val;
// 加载模块$engine = new Engine();$module = Module::fromFile($engine, 'math.wasm');$store = new Store($engine);
// 创建实例$instance = new Wasmtime\\Instance($store, $module, []);
// 获取导出的函数$add = $instance->getExport($store, 'add');$factorial = $instance->getExport($store, 'factorial');
// 调用函数$resultAdd = $add->call($store, 5, 7);$resultFact = $factorial->call($store, 10);
echo "5 + 7 = " . $resultAdd[0]->i32() . ""; // 12echo "10! = " . $resultFact[0]->i32()