0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust製PHP拡張の配布方法を考える――Composer、PIE、Windowsバイナリの課題

0
Posted at

対象読者

この記事は、RustでPHP拡張を開発してみたいPHPエンジニアや、Composer・PIEを使ったネイティブ拡張の配布方法に関心がある人を対象にしています。

RustやPHP拡張の実装方法そのものよりも、次の点を知りたい読者を想定しています。

  • Rust製PHP拡張を利用者へどう届けるか
  • Windows、macOS、Linux向けの配布をどう分けるか
  • ComposerとPIEの責務をどう整理するか
  • PythonのWheelやuvから何を学べるか
  • 個人開発で無理のない配布範囲をどう決めるか

Rust、PHP Internals、OSごとのビルド環境に詳しくなくても読めるように、細かな実装コードより配布設計と運用上の判断を中心に扱います。

PythonのWheelとuvから考える、ComposerとPIEの役割

RustでPHP拡張を開発することは、以前より身近になりました。

ext-php-rsのようなライブラリを使えば、Rustの構造体や関数をPHPへ公開できます。HTTP解析、データ検証、シリアライズなど、ネイティブコードを活用できそうな処理もいろいろ考えられます。

しかし、拡張をビルドして.so.dllを作ったところで、すぐに一般利用できるわけではありません。

利用者の環境に合うバイナリを、誰が作り、どこへ置き、どうやって選ぶのでしょうか。

Rust製PHP拡張を広げるうえでは、実装より先に配布の問題を考える必要があります。

PythonではWheelがRustを隠してくれる

Pythonでは、Rust製のモジュールや開発ツールが珍しくなくなりました。

Pydanticはデータ検証やシリアライズの中核にRustを使っています。Polarsはデータ処理基盤をRustで実装しています。RobynやGranianはWebフレームワークやアプリケーションサーバーの領域でRustを活用しています。

RuffはLintやFormatterをRustで実装し、uvは依存解決、仮想環境、Python本体の管理まで扱います。

ここで重要なのは、Python利用者が通常Rustをビルドしないことです。

Rustコード
    ↓
PyO3
    ↓
maturin
    ↓
Wheel
    ↓
PyPI
    ↓
pip / uv

作者はCIでWindows、macOS、Linux向けのWheelを作ります。

PyPIは完成したWheelを保存し、pipやuvが利用者の環境に合うものを選びます。対応するWheelがなければ、ソースからのビルドへ進むこともできます。

PyPIが作者の代わりに全部ビルドしてくれるわけではありません。

役割が分かれているのです。

責務 Pythonで使われる仕組み
ビルド方法の宣言 pyproject.toml
PythonとRustの接続 PyO3
Rust拡張のビルド maturin
複数環境向けの生成 cibuildwheel
ビルドの実行 GitHub Actionsなど
成果物の保存 PyPI
適合する成果物の選択 pip、uv

uvは配布環境までRustで作り直した

PydanticやPolarsは、Pythonから呼び出されるRust製ライブラリです。

uvは少し立場が違います。

uvが扱うのは、ライブラリ内部の処理だけではありません。

  • Python本体の導入
  • 仮想環境
  • 依存関係の解決
  • ロックファイル
  • パッケージのインストール
  • CLIツールの実行
  • パッケージのビルドと公開

つまり、Pythonで起きている変化は「一部の重い処理をRustへ移した」という話にとどまりません。

RustでPythonモジュールを作る
            ↓
RustでPythonの開発環境も支える

この変化はPythonだけで起きているものでもありません。

LinuxカーネルではRustがメインラインで利用可能になり、DenoはRustをランタイム基盤に使っています。Viteも、Rust製のRolldownやOxcを中核処理へ取り込んでいます。

ここで起きているのは、既存言語をRustで全面的に置き換えることではありません。

利用者は今までどおりPython、JavaScript、TypeScriptを書きます。その裏側で、解析、検証、I/O、バンドル、依存解決、パッケージ管理などの基盤がRustへ移っています。

PHPでも、Rustを採用できるかどうかより、Rust製の基盤を既存のPHP開発体験へどう組み込むかが問題になります。

PHP拡張は、Windowsだけバイナリを配るところから始める

PHP拡張は、OSとCPUが一致するだけでは利用できません。

少なくとも、次の条件を区別する必要があります。

OS
× CPU
× PHPバージョン
× Zend Module API
× NTS / ZTS
× debug / nodebug
× glibc / musl

Linuxではglibcとmuslが分かれ、WindowsではNTSとZTSが分かれます。PHP 8.4とPHP 8.5の両方を支えるなら、成果物もPHPバージョンごとに必要です。

最初からWindows、macOS、Linuxの全組み合わせへバイナリを用意すると、個人開発ではビルドとロードテストの負担が大きくなります。

そこで、初期段階では配布方法を分けます。

Windows
    検証済みDLLを配布する

Linux
    利用者の環境またはCIでソースビルドする

macOS
    利用者の環境またはCIでソースビルドする

Windowsを優先する理由は、PHP SDK、Visual Studio、MSVC、import library、NTS/ZTSなど、ビルド環境の前提が多いからです。

LinuxやmacOSにもRust、PHP開発ヘッダー、Cコンパイラーは必要ですが、DockerやCIのビルド工程へ組み込みやすく、本番サーバーで直接ビルドする必要はありません。

最初は、需要の高いWindows x86_64 NTS向けDLLだけを公開します。LinuxとmacOSでは、対応バイナリがなければソースビルドへフォールバックします。

PIE
    ↓
対応するWindows DLLがある
    ↓
ダウンロードして導入

PIE
    ↓
対応バイナリがない
    ↓
ソースからビルド

この方式なら、すべての互換マトリクスを最初から背負わずに、バイナリ選択、ダウンロード、検証、ロードまでの配布経路を試せます。

Composerは宣言、PIEは導入

PHPでは、ComposerとPIEの役割を分けて考える必要があります。

Composerは、PHPプロジェクトの依存関係を管理します。

composer.jsonでは、必要な拡張を次のように宣言できます。

{
  "require": {
    "php": "^8.5",
    "ext-example": "^1.0"
  }
}

しかし、Composerはext-exampleの実体をPHPランタイムへインストールしません。

Composerが扱うのは、主にプロジェクトの依存関係です。

PHP拡張は、プロジェクトのvendorディレクトリではなく、PHPランタイムへ読み込まれます。

ここを担当するのがPIEです。

Composer
- 必要な拡張を宣言する
- バージョン制約を管理する
- PHPライブラリとの依存関係を解決する

PIE
- 実行環境を調べる
- 適合する拡張を選ぶ
- バイナリを取得する
- 必要ならソースからビルドする
- PHPランタイムへ導入する

CI
- 各環境向けにビルドする
- PHPへロードできるか確認する
- チェックサムを作る
- リリースへ公開する

Pythonに置き換えると、次のような対応です。

Python PHP
pyproject.toml composer.json
Wheel PHP拡張のバイナリアーカイブ
PyPI GitHub Releasesなど
pip / uv PIE
cibuildwheel PHP拡張向けCIビルダー
sdistからのビルド ソースビルドへのフォールバック

Composerへバイナリの選択、PHP設定の変更、ランタイムへの導入まで押し込むと、責務が混ざります。

個人実験では小さな選択器を作ればよい

PIE向けの正式な配布へ進む前に、個人プロジェクトで小さな参照実装を作れます。

composer.jsonextraへ、独自のバイナリ情報を書きます。

{
  "extra": {
    "native-extensions": {
      "example_ext": {
        "version": "0.1.0",
        "artifacts": [
          {
            "php": "8.5",
            "os": "windows",
            "arch": "x86_64",
            "thread-safety": "nts",
            "url": "https://example.invalid/example_ext.zip",
            "sha256": "..."
          }
        ]
      }
    }
  },
  "scripts": {
    "native-ext:install": "@php tools/install-native-extension.php"
  }
}

専用スクリプトは、次だけを担当します。

  1. 現在のPHP環境を調べる
  2. 対応するバイナリを探す
  3. WindowsならDLLをダウンロードする
  4. SHA-256を検証する
  5. LinuxとmacOSならソースビルドへ進む
  6. 別プロセスで拡張をロードする
  7. 結果を独自のロックファイルへ記録する

最初からpost-install-cmdへ接続する必要はありません。

composer native-ext:install

明示的なコマンドとして動かし、安定してから自動実行を検討できます。

これは正式な標準ではありません。

しかし、一つの拡張を選択し、取得し、検証し、ロードするところまで作れば、PIEに何が不足しているのかを具体的に確認できます。

まとめ

Rustはすでに、Linuxカーネル、Pythonモジュール、JavaScriptランタイム、フロントエンドツール、パッケージ管理ツールで使われています。

PHPでもRust製拡張を開発する技術はあります。

不足しているのは、Rustを採用する理由ではありません。

ext-php-rs
    Rust製拡張を開発する

Composer
    拡張の必要性を宣言する

GitHub Actions
    拡張をビルドしてテストする

GitHub Releases
    バイナリを保存する

PIE
    適合する成果物を選んで導入する

これらを、一つの導入経路としてつなぐ必要があります。

最初からすべてのOS、CPU、PHPバージョンへバイナリを配る必要はありません。

Windowsだけ検証済みDLLを配り、LinuxとmacOSはソースビルドへフォールバックするところから始められます。

Rustで何を作るかより前に、作ったものをどう届けるかを試す。

PHPでRust製拡張を広げるうえでは、その順番が重要です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?