From 4681e861efcdf9b0b73e3803e70712bc55162863 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sat, 8 Aug 2026 10:38:18 +0900 Subject: feat(slides): improve slide controller --- .../compile-php-runtime-to-wasm/index.html | 252 +++++++++++---------- 1 file changed, 131 insertions(+), 121 deletions(-) (limited to 'services/nuldoc/public/blog/posts/2023-10-02/compile-php-runtime-to-wasm/index.html') diff --git a/services/nuldoc/public/blog/posts/2023-10-02/compile-php-runtime-to-wasm/index.html b/services/nuldoc/public/blog/posts/2023-10-02/compile-php-runtime-to-wasm/index.html index 176793d5..83ab96ca 100644 --- a/services/nuldoc/public/blog/posts/2023-10-02/compile-php-runtime-to-wasm/index.html +++ b/services/nuldoc/public/blog/posts/2023-10-02/compile-php-runtime-to-wasm/index.html @@ -15,7 +15,7 @@ PHP の処理系を Emscripten で WebAssembly にコンパイルする|REPL: Rest-Eat-Program Loop - +
@@ -142,18 +142,19 @@
index.mjs
-
import { readFile } from 'node:fs/promises'; -
import PHPWasm from './php-wasm.mjs' -
-
const code = await readFile('/dev/stdin', { encoding: 'utf-8' }); -
-
const { ccall } = await PHPWasm(); -
const result = ccall( -
'php_wasm_run', -
'number', ['string'], -
[code], -
); -
console.log(`exit code: ${result}`);
+
import { readFile } from 'node:fs/promises';
+import PHPWasm from './php-wasm.mjs'
+
+const code = await readFile('/dev/stdin', { encoding: 'utf-8' });
+
+const { ccall } = await PHPWasm();
+const result = ccall(
+  'php_wasm_run',
+  'number', ['string'],
+  [code],
+);
+console.log(`exit code: ${result}`);
+

標準入力から与えたコードを WebAssembly にコンパイルされた PHP 処理系の上で実行している。このような php-wasm.mjs (とそこから呼び出される php-wasm.wasm) を作成する。 @@ -167,30 +168,31 @@ 先ほどのコードでも使っていたエントリポイントである php_wasm_run を用意する。

-
#include <stdio.h> -
#include <emscripten.h> -
#include <Zend/zend_execute.h> -
#include <sapi/embed/php_embed.h> -
-
int EMSCRIPTEN_KEEPALIVE php_wasm_run(const char* code) { -
zend_result result; -
-
int argc = 1; -
char* argv[] = { "php.wasm", NULL }; -
-
PHP_EMBED_START_BLOCK(argc, argv); -
-
result = zend_eval_string_ex(code, NULL, "php.wasm code", 1); -
-
PHP_EMBED_END_BLOCK(); -
-
fprintf(stdout, "\n"); -
fflush(stdout); -
fprintf(stderr, "\n"); -
fflush(stderr); -
-
return result == SUCCESS ? 0 : 1; -
}
+
#include <stdio.h>
+#include <emscripten.h>
+#include <Zend/zend_execute.h>
+#include <sapi/embed/php_embed.h>
+
+int EMSCRIPTEN_KEEPALIVE php_wasm_run(const char* code) {
+    zend_result result;
+
+    int argc = 1;
+    char* argv[] = { "php.wasm", NULL };
+
+    PHP_EMBED_START_BLOCK(argc, argv);
+
+    result = zend_eval_string_ex(code, NULL, "php.wasm code", 1);
+
+    PHP_EMBED_END_BLOCK();
+
+    fprintf(stdout, "\n");
+    fflush(stdout);
+    fprintf(stderr, "\n");
+    fflush(stderr);
+
+    return result == SUCCESS ? 0 : 1;
+}
+

ほとんどはただの PHP の公開 API を使ったコードだが、Emscripten 向けの注意点が 2点ある。 @@ -213,15 +215,16 @@ デフォルトの出力方法は index.mjs の中で PHPWasm() を呼ぶとき、stdoutstderr というオプションを渡せば変更できる。

-
const { ccall } = await PHPWasm({ -
stdout: (c) => { -
if (c === null) { -
// flush the standard output. -
} else { -
// output c to the standard output. -
} -
}, -
});
+
const { ccall } = await PHPWasm({
+  stdout: (c) => {
+    if (c === null) {
+      // flush the standard output.
+    } else {
+      // output c to the standard output.
+    }
+  },
+});
+

cnull か 1バイト符号つき整数を取り、null が flush 要求を意味する。 @@ -241,49 +244,52 @@ まずは Emscripten 公式が提供している Docker イメージ を使って、PHP 処理系と先ほど示した C 言語のソースコードを WebAssembly にコンパイルする。

-
FROM emscripten/emsdk:3.1.46 AS wasm-builder
+
FROM emscripten/emsdk:3.1.46 AS wasm-builder
+

次に、 php/php-src から PHP 処理系のソースコードを取得し、ビルドに必要な apt パッケージを取ってくる。有効にする拡張を増やしたいなら、ここでインストールするパッケージも増やすことになるだろう。

-
RUN git clone --depth=1 --branch=php-8.2.10 https://github.com/php/php-src -
-
RUN apt-get update && \ -
apt-get install -y --no-install-recommends \ -
autoconf \ -
bison \ -
pkg-config \ -
re2c \ -
&& \ -
:
+
RUN git clone --depth=1 --branch=php-8.2.10 https://github.com/php/php-src
+
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends \
+        autoconf \
+        bison \
+        pkg-config \
+        re2c \
+        && \
+    :
+

続けて、Emscripten のツールチェインを用いて PHP 処理系をビルドする。

-
RUN cd php-src && \ -
./buildconf --force && \ -
emconfigure ./configure \ -
--disable-all \ -
--disable-mbregex \ -
--disable-fiber-asm \ -
--disable-cli \ -
--disable-cgi \ -
--disable-phpdbg \ -
--enable-embed=static \ -
--enable-mbstring \ -
--without-iconv \ -
--without-libxml \ -
--without-pcre-jit \ -
--without-pdo-sqlite \ -
--without-sqlite3 \ -
&& \ -
EMCC_CFLAGS='-s ERROR_ON_UNDEFINED_SYMBOLS=0' emmake make -j$(nproc) && \ -
mv libs/libphp.a .. && \ -
make clean && \ -
git clean -fd && \ -
:
+
RUN cd php-src && \
+    ./buildconf --force && \
+    emconfigure ./configure \
+        --disable-all \
+        --disable-mbregex \
+        --disable-fiber-asm \
+        --disable-cli \
+        --disable-cgi \
+        --disable-phpdbg \
+        --enable-embed=static \
+        --enable-mbstring \
+        --without-iconv \
+        --without-libxml \
+        --without-pcre-jit \
+        --without-pdo-sqlite \
+        --without-sqlite3 \
+        && \
+    EMCC_CFLAGS='-s ERROR_ON_UNDEFINED_SYMBOLS=0' emmake make -j$(nproc) && \
+    mv libs/libphp.a .. && \
+    make clean && \
+    git clean -fd && \
+    :
+

ここまでと比べると少し複雑なので、それぞれ詳しく見ていこう。 @@ -307,22 +313,23 @@ さて、PHP 処理系をライブラリ化できたので、次に先ほど載せた C のソースコードをビルドしていこう。Dockerfile と同じ場所に php-wasm.c という名前で保存し、次のようにする。

-
COPY php-wasm.c /src/ -
-
RUN cd php-src && \ -
emcc \ -
-c \ -
-o php-wasm.o \ -
-I . \ -
-I TSRM \ -
-I Zend \ -
-I main \ -
../php-wasm.c \ -
&& \ -
mv php-wasm.o .. && \ -
make clean && \ -
git clean -fd && \ -
:
+
COPY php-wasm.c /src/
+
+RUN cd php-src && \
+    emcc \
+        -c \
+        -o php-wasm.o \
+        -I . \
+        -I TSRM \
+        -I Zend \
+        -I main \
+        ../php-wasm.c \
+        && \
+    mv php-wasm.o .. && \
+    make clean && \
+    git clean -fd && \
+    :
+

emcccc (C コンパイラ/リンカ) の Emscripten 版で、-c は「コンパイル」の意。-o-I は普通の C コンパイラと同様、出力ファイルの指定とインクルードパスの指定である。 @@ -331,18 +338,19 @@ libphp.aphp-wasm.o が手に入ったので、これらをリンクして WebAssembly のバイナリとそのラッパである JavaScript ファイルを生成する。これにも emcc コマンドを使う。

-
RUN emcc \ -
-s ENVIRONMENT=node \ -
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \ -
-s EXPORTED_RUNTIME_METHODS='["ccall"]' \ -
-s EXPORT_ES6=1 \ -
-s INITIAL_MEMORY=16777216 \ -
-s INVOKE_RUN=0 \ -
-s MODULARIZE=1 \ -
-o php-wasm.js \ -
php-wasm.o \ -
libphp.a \ -
;
+
RUN emcc \
+    -s ENVIRONMENT=node \
+    -s ERROR_ON_UNDEFINED_SYMBOLS=0 \
+    -s EXPORTED_RUNTIME_METHODS='["ccall"]' \
+    -s EXPORT_ES6=1 \
+    -s INITIAL_MEMORY=16777216 \
+    -s INVOKE_RUN=0 \
+    -s MODULARIZE=1 \
+    -o php-wasm.js \
+    php-wasm.o \
+    libphp.a \
+    ;
+

それぞれのフラグについて解説する。 @@ -375,14 +383,15 @@ といっても、Node.js はビルトインで WebAssembly をサポートしているので、ほとんどやることはない。先ほど掲載した JavaScript のコードは、Dockerfile と同じディレクトリに index.mjs で配置すること。

-
FROM node:20.7 -
-
WORKDIR /app -
COPY --from=wasm-builder /src/php-wasm.js /app/php-wasm.mjs -
COPY --from=wasm-builder /src/php-wasm.wasm /app/php-wasm.wasm -
COPY index.mjs /app/ -
-
ENTRYPOINT ["node", "index.mjs"]
+
FROM node:20.7
+
+WORKDIR /app
+COPY --from=wasm-builder /src/php-wasm.js /app/php-wasm.mjs
+COPY --from=wasm-builder /src/php-wasm.wasm /app/php-wasm.wasm
+COPY index.mjs /app/
+
+ENTRYPOINT ["node", "index.mjs"]
+
@@ -392,12 +401,13 @@ Dockerfilephp-wasm.cindex.mjs を用意したら、Docker コンテナをビルドして実行する。

-
$ docker build -t php-wasm . -
$ echo 'echo "Hello, World!", PHP_EOL;' | docker run --rm -i php-wasm -
Hello, World! -
-
-
exit code: 0
+
$ docker build -t php-wasm .
+$ echo 'echo "Hello, World!", PHP_EOL;' | docker run --rm -i php-wasm
+Hello, World!
+
+
+exit code: 0
+
-- cgit v1.3.1