PHPからWebAssemblyを動かしてみる

こんにちは、コバヤシです。 先月、InfiniCloud株式会社様の勉強会で「PHPとWebAssembly」についてお話ししてきました。
(このあたりの話はドイが前回のブログにて書いています)

tech.arms-soft.co.jp

この時にはWebAssemblyとしてPHPを使う方法について話しをしたのですが、今回はその時に時間がなくてお話しできなかった「PHPからWebAssembly」を動かす方法について書きたいと思います。

Wasmerのインストール

まずは簡単にWebAssemblyを実行できるように、Wasmerをインストールします。

wasmer.io

今回は、Apacheから使用できるように/usr/local/bin/に入れます。

ダウンロード
curl -LO https://github.com/wasmerio/wasmer/releases/latest/download/wasmer-linux-amd64.tar.gz
  
解凍
tar -xzf wasmer-linux-amd64.tar.gz
  
移動
mv bin/wasmer /usr/local/bin/wasmer

次にApacheが設定にアクセスできるようにします。

mkdir -p /etc/wasmer
chown apache:apache /etc/wasmer

そして環境変数にパスを設定します。
/etc/systemd/system/httpd.service.d/override.conf

[Service]
Environment="WASMER_DIR=/etc/wasmer"

最後に設定を反映して再起動。

systemctl daemon-reload
systemctl restart httpd

RUSTでWebAssemblyを作成

RUSTでWebAssemblyを作成します。今回は、単純に2つの数値を渡して合計するプログラムです。

まずcagoでプロジェクトを作成します。

cargo new --lib sum

sum/Cargo.tomlを編集します。

[package]
name = "sum-wasm"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib"]

[dependencies]

合計するプログラムを書きます。
src/lib.rs

#[no_mangle]
pub extern "C" fn sum(x: i32, y: i32) -> i32 {
    x + y
}

ビルドをします。 ターゲットは「wasm32-unknown-unknown」を指定します。

cargo build --target wasm32-unknown-unknown --release

sum/target/wasm32-unknown-unknown/release/sum_wasm.wasmにファイルが生成されているのでこれを使います。

PHPでWebAssemblyを使う

以下のようなプログラムを書いてPHPからWebAssemblyを使ってみます。

wasm.php

<?php

// ファイルパス
$wasmFile = '/xxxx/xxxx/xxxx/sum_wasm.wasm';

// パラメーター
$param1 = 100;
$param2 = 200;

// コマンド
$command = "wasmer run {$wasmFile} --invoke sum -- {$param1} {$param2}";

// 実行
$output = shell_exec($command);

echo '実行結果: ' . $output;

ブラウザからアクセスすると、、、

無事計算結果が出ました!

RUSTのプログラムを直接実行してみる

PHPからWebAssemblyを実行できることは確認できましたが、以前から抱いていた疑問が再び頭をよぎりました。それは「直接Rustのプログラムを起動してもいいのではないか?」ということです。

この際なので、試してみようと思います。

基本は同じですが、プロジェクトはbinで作成してビルドはx86_64-unknown-linux-gnuとしました。
targetは実行するサーバーによって変えることになります。

rustのプログラムは以下のようにします。

src/main.rs

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();

    if args.len() != 3 {
        eprintln!("Usage: <program_name> <num1> <num2>");
        std::process::exit(1);
    }

    let x: i32 = args[1].parse().expect("Invalid number");
    let y: i32 = args[2].parse().expect("Invalid number");

    let result = sum(x, y);
    println!("{}", result);
}

fn sum(x: i32, y: i32) -> i32 {
    x + y
}

phpはwasmerではなく直接呼び出すように変更します。

<?php

// ファイルパス
$rustFile = '/xxx/xxx/xxx/sum';

// パラメーター
$param1 = 100;
$param2 = 200;

// コマンド
$command = "{$rustFile} {$param1} {$param2}";

// 実行
$output = shell_exec($command);

echo 'RUSTからの実行結果: ' . $output;

こちらの方法でも問題なく計算結果が表示されました。

まとめ

今回、PHPからWebAssemblyを動かす方法と、Rustのプログラムを直接実行する方法を試してみました。WebAssemblyはセキュリティが高く、さまざまな環境で動作する利点がありますが、少しセットアップが必要です。一方、Rustを直接実行する方法は、WebAssemblyに比べてセットアップが簡単ですが、環境ごとにビルドが必要で柔軟性に欠ける場合があると感じました。
PHPから使う際には、どちらも同様の使い勝手なので、どちらの方法を選ぶかはプロジェクトや環境の要件によって使い分けるのが良いと感じました。
機会があったら、RustとPHPのさらなる連携についても掘り下げてみたいと思います。