FlowingDev

PHP、プラグ抜け: ブラウザタブが君のパーソナルサーバーに

WebAssemblyが、どうやって本物のPHPインタプリタをブラウザで直接動かすのか見てみよう。サーバー設定なしで、即座のテストやプロトタイピングが可能になるんだ。

ツールを試す: PHP Playground

一言でいうと

PHPプレイグラウンドは、君の書いたスクリプトを実行する正真正銘のPHPインタプリタを、WebAssemblyっていうブッ飛んだ技術を使って、ウェブブラウザ上で直接動かすものだ。サーバーは一切不要。

こいつが解決する問題

何十年もの間、PHPを動かすってことは、つまりサーバーをセットアップするってことだった。データセンターにある物理マシンだろうが、クラウド上の仮想プライベートサーバーだろうが、はたまた手元のラップトップに入れたXAMPP/MAMP/Dockerみたいなローカル環境だろうが、とにかく専用の環境が必要だった。PHPは、その定義からしてサーバーサイド言語だったんだ。

これが、単純なタスクに山ほど手間を生み出していた。新しい配列関数を試したい?json_encodeが特定のオブジェクトをどう処理するか確認したい?厄介な正規表現をデバッグしたい?そんなときも、ローカルサーバーを起動し、新しい.phpファイルを作り、コードを書いて、ブラウザで開くかコマンドラインから実行する必要があった。それは、パン一枚をトーストするためだけに、業務用のオーブンを予熱するようなものだった。

この「セットアップ税」は、特に次のような人たちにとって大きな苦痛だった。

  • 学習者: 初心者は、最初のecho "Hello, World!";を書く前に、まずサーバー設定と格闘しなきゃならなかった。
  • サクッと試したい人: 「思考のメモ帳」的な環境が必要な熟練開発者も、30秒で終わるタスクのために何ステップものプロセスを踏む必要があった。
  • 共同作業者: コードスニペットを共有するってのは、テキストを貼り付けて、相手が互換性のある実行環境を持っていることを祈る、ってことだった。

根本的な問題は、PHPという言語と、それが動作するサーバー環境がガッチリ結びついちゃってたことだ。PHPインタプリタをサーバーから切り離して、ポータブルで、すぐにアクセスできるようにする必要があった。そこで登場したのがWebAssemblyだ。

舞台裏の仕組み

サーバーサイド言語をクライアントサイドのブラウザで動かすことを可能にする魔法、それがWebAssembly、略して「Wasm」だ。これはブラウザに何ができるかっていう根本的な常識を覆すもので、PHPプレイグラウンドはそのパワーを示す素晴らしい一例なんだ。

WebAssembly (Wasm) の魔法

君のブラウザのJavaScriptエンジンを、ある一つの言語、つまりJavaScriptをめちゃくちゃ得意とする、超専門的な職人だと考えてみてほしい。何年もの間、そいつが話せる言語はそれだけだった。ブラウザでコードを動かしたければ、JavaScript(もしくはTypeScriptみたいにJavaScriptにコンパイルされる言語)で書くしかなかったんだ。

WebAssemblyがそのゲームのルールを変えた。これは低レベルなバイナリ命令形式で、手で書くような言語というよりは、コンパイルターゲットに近い。例えるなら、IKEAの説明書一式を送る代わりに、あらかじめ組み立てられた機械部品を出荷するようなものだ。C、C++、Rustのような言語は、.wasmファイルにコンパイルできる。ブラウザはこのファイルを受け取って、JavaScriptを解釈実行するよりもはるかに高速な、ほぼネイティブに近いスピードで実行できる。

重要なのは、WasmはJavaScriptと同じ安全なサンドボックス内で実行されることだ。つまり、君のコンピュータ上のファイルに勝手にアクセスしたり、OSをいじったりすることはできない。

PHPをWasmにコンパイルする

じゃあ、これがどうPHPに応用されるのか?標準的なPHPインタプリタであるZend Engineは、C言語で書かれている。これが鍵だ。インタプリタがC言語のプログラムであるということは、特別なツールチェインを使えば、WindowsやLinux向けの従来の実行ファイルの代わりに、WebAssemblyにコンパイルできるってことなんだ。

この仕事で最もよく使われるツールがEmscriptenだ。これはPHPのソースコードを受け取って、2つのことを行う。

  1. Cコードを.wasmファイルにコンパイルする。 これがPHPエンジンのコア部分で、ブラウザが実行できる形式になったものだ。
  2. JavaScriptの「グルー(接着剤)」コードを生成する。 このJSコードは、Wasmの世界とブラウザのJavaScriptの世界との間の橋渡し役となる。Wasmモジュールの読み込み、データのやり取り、入力と出力の接続などを担当する。

簡略化したパイプラインはこんな感じだ。 PHPのCソースコード -> Emscriptenコンパイラ -> php.wasm + glue.js

ミニOSとしてのブラウザ

PHPプレイグラウンドのページを読み込むと、君のブラウザはPHPインタプリタのための一時的で自己完結したOSになる。

  1. 取得: ブラウザがphp.wasmファイルとJavaScriptのグルーコードをダウンロードする。
  2. インスタンス化: グルーコードがブラウザのJavaScriptエンジンにWasmモジュールを読み込むよう指示する。これにより、メモリ上にPHPインタプリタのインスタンスが作成される。
  3. 仮想環境: ここが巧妙なところだ。PHPはファイルの読み込みや標準出力への書き込みができることを期待している。グルーコードは、そのために仮想環境を作り出す。
    • 標準出力 (stdout): 君のPHPスクリプトがechoやprintを使うとき、Wasmモジュールはターミナルに書き出すんじゃない。代わりに、JavaScriptのグルーコードが読み取れるメモリバッファに書き込む。そして、プレイグラウンドのUIがこの出力を受け取って、画面に表示するんだ。
    • 仮想ファイルシステム: Emscriptenはメモリ内にファイルシステム(よくMEMFSと呼ばれる)を提供する。君のPHPコードがfile_put_contents('data.txt', '...')を実行しようとしても、それは君のハードドライブに書き込まれるわけじゃない。ブラウザのメモリ内にあるJavaScriptオブジェクトの中に作られた、シミュレートされたファイルに書き込んでいるんだ。このファイルは、そのページのセッションが有効な間だけ存在する。

君がPHPコードを入力して「実行」を押すと、プレイグラウンドのJavaScriptが君のスクリプトを文字列としてWasmインスタンスに渡す。コンパイル済みのPHPエンジンがそれを実行し、その出力がJSグルーによってキャプチャされ、結果が画面に表示される。そのすべてが、たった一つのブラウザタブの中で完結するんだ。

// プレイグラウンドのJS側の概念的なコード
async function runMyPhp() {
  // 1. WasmモジュールとそのJSブリッジを読み込む
  const php = await createPHPWasmModule();

  // 2. エディタから持ってきたPHPコード
  const userCode = '<?php echo "The answer is " . (6 * 7); ?>';

  // 3. ブリッジ経由でコードを実行
  const output = php.run(userCode);

  // 4. キャプチャした出力を表示
  document.getElementById('output-area').textContent = output;
  // -> "The answer is 42" と表示される
}

実世界でのストーリー

サクッとアルゴリズムチェック

Devonはチームメイトのプルリクエストをレビューしている。コードには、array_mapとarray_filterを使ってユーザーデータを変換する、いかにも厄介そうな関数が含まれていた。ロジックが込み入っていて、Devonはすべてのエッジケースをカバーできているか100%の自信が持てない。昔ながらのやり方なら、ブランチをプルして、composer installを走り、ローカルサーバーを立ち上げ、一時的なテストスクリプトを書き、それを実行する、という流れだ。新しいやり方はこうだ。10行の関数をコピーして、PHPプレイグラウンドに貼り付け、サンプル配列を定義して、実行ボタンをクリック。5秒で結果が表示される。彼はnull値を含む別のサンプル配列を試してみる。すると出力は…エラーだ。この関数は思ったほど堅牢ではなかった。彼は修正したコードとプレイグラウンドへのリンクをコードレビューのコメントに貼り付けた。

教訓: プレイグラウンドは、独立したロジックをテストするための「心理的な障壁」を数分から数秒にまで下げ、より厳密な検証を促してくれる。

外出先でのドキュメント修正

Samは電車の中で、スマホでとあるオープンソースPHPライブラリのドキュメントを眺めている。PDFを作成するコード例が古くなっていることに気づいた。ラップトップもちゃんとした開発環境もないので、普通なら後で確認しようと心にメモするしかない。しかし彼女は代わりに、スマホのブラウザでPHPプレイグラウンドを開き、そのコード例を貼り付けて実行した。案の定、非推奨の警告が出た。彼女はプレイグラウンド上で直接コードをいじってクリーンに実行されるように直し、そのままスマホのブラウザからドキュメントのプルリクエストを送信した。

教訓: プレイグラウンドは開発を特定のPCから切り離し、ウェブブラウザさえあればどこからでもテスト、デバッグ、そしてコントリビュートすることを可能にする。

ライブ学習セッション

講師のMariaは、PHP入門コースを教えている。これまでは、最初の1週間は悪夢だった。Windows, Mac, Linuxと様々なマシンを使う生徒全員に、ローカルサーバー環境をインストール・設定させるのにてんてこ舞いだったからだ。しかし今年は、彼女はクラスにPHPプレイグラウンドのリンクを1つ送るだけだ。彼女が画面上でライブコーディングをすると、生徒一人ひとりが自分のデバイスで全く同じコードを即座に実行し、修正できる。「もしダブルクォートの代わりにシングルクォートを使ったらどうなりますか?」と生徒が質問すれば、彼らはすぐに自分で試すことができ、発見の感覚と即時のフィードバックを育むことができる。

教訓: プレイグラウンドは、セットアップの障壁を取り除き、学習をインタラクティブで実験的なプロセスに変える、驚くべき教育ツールだ。

よくある間違いと落とし穴

  • 完全なウェブサーバーを期待してしまうこと。 PHPプレイグラウンドはApacheやNginxの代替品ではない。これはPHPをコマンドライン(CLI)の文脈で実行している。つまり、$_GET、$_POST、$_SERVERのようなスーパーグローバル変数は、それらを提供するHTTPリクエストが存在しないため、値がセットされない。
  • その場限りのファイルシステムであることを忘れること。 file_put_contents()でファイルを書き込んでも、それは一時的なメモリ上のファイルシステムに存在するだけだ。君のコンピュータには保存されない。ページをリフレッシュすれば、そのファイルは永遠に消えてしまう。
  • ブラウザのリソース制限を無視すること。 無限ループや大量のメモリを消費するスクリプトは、サーバーを遅くするだけでなく、ブラウザのタブ自体をフリーズさせたりクラッシュさせたりする可能性がある。
  • ネットワーク関数が当たり前に動くと思い込むこと。 file_get_contents('https://api.example.com')やcURL操作のような、外部ネットワークにリクエストを出すPHP関数は、ブラウザのセキュリティモデルに従う。プレイグラウンドにそれらを処理するための特別なプロキシやシムが含まれていない限り、CORS (オリジン間リソース共有) ポリシーによってブロックされる。
  • 特定のPHP拡張機能に頼ること。 WasmにコンパイルされたPHPインタプリタには、君が使い慣れているすべての拡張機能(例えば、画像操作のためのgd、デバッグ用のxdebug、pdo_mysqlのようなデータベースドライバなど)が含まれているとは限らない。その機能は、その特定のWasmビルドにコンパイルされた拡張機能に限定される。

なぜ注目すべきなのか

PHPに関する疑問が「もし〜したらどうなるんだろう…」で始まったときはいつでも、PHPプレイグラウンドを思い出すべきだ。これは現代のPHP開発者にとって、究極の「思考のメモ帳」なのだ。

君の次のアプリケーションを構築するためのものではない。でも、その合間の瞬間において、なくてはならないツールだ。

  • プロトタイピング: 関数やクラスの骨子を素早く作り上げる。
  • デバッグ: バグのあるロジックをアプリの他の部分から切り離して検証する。
  • 学習: セットアップなしで、新しい言語機能や関数を試す。
  • 共有: 同僚やバグレポートで共有するために、実行可能なコードのサンプルを作成する。

複雑な開発環境が当たり前の世界で、PHPプレイグラウンドは一服の清涼剤だ。ただちょっとPHPを書きたい、という願いを、シンプルに、即座に、そしてパワフルに叶えてくれる。

さらに深く知る

  • WebAssembly Official Site: Wasmに関するあらゆることの「信頼できる唯一の情報源」。
  • MDN WebAssembly Docs: Mozillaによる、ブラウザでWasmがどのように動作するかについての、素晴らしく分かりやすいドキュメント。
  • Emscripten Documentation: PHPのようなC/C++プロジェクトをWebAssemblyにコンパイルするために使われるツールチェインの公式ドキュメント。
  • PHP.net: PHPの公式マニュアル。プレイグラウンドはこのエンジンを動かしているので、各関数が何をするか調べるならここがリファレンスだ。
  • The php-wasm Project: Wasm経由でPHPをブラウザで動かすという取り組みを開拓した、中心的なオープンソースプロジェクトの一つ。

エディターはどうコードを理解するのか

色分けはプログラムの実行ではなく、言語パーサーによるものです。CodeMirror と Lezer がブラウザー内で記号と構文を解析します。候補にはキーワード、対応する場合のローカル名、スニペット、小さな組み込みリファレンスが含まれます。登録済みのシンボルにカーソルを重ねるとシグネチャが表示されます。候補が出ても、その API が実行環境に存在する保証はありません。対応するパーサーは構文エラーを検出しますが、型検査や実行の代わりにはなりません。Ruby と Lua はトークンベースのモードで、診断は限定的です。リモートの言語サーバーは使わず、コードは端末内に留まります。

CodeMirror & Lezer: MIT.

理論はOK。さあ手を動かそう — 100%ブラウザ内で。

ツールを試す: PHP Playground