docs: add Japanese translation for documentation (#1740)

* docs(ja): add Japanese documentation translation

* docs(ja): fix invalid link fragments and improve section heading
This commit is contained in:
demouth
2025-08-25 23:15:10 +09:00
committed by GitHub
parent 1eb16f1434
commit 53e6d1897d
19 changed files with 2884 additions and 0 deletions

219
docs/ja/CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,219 @@
# コントリビューション
## PHPのコンパイル
### Dockerを使用する場合Linux
開発用Dockerイメージをビルドします
```console
docker build -t frankenphp-dev -f dev.Dockerfile .
docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -p 443:443/udp -v $PWD:/go/src/app -it frankenphp-dev
```
このイメージには通常の開発ツールGo、GDB、Valgrind、Neovimなどが含まれており、PHP設定ファイルは以下の場所に配置されます。
- php.ini: `/etc/frankenphp/php.ini` 開発用のプリセットが適用されたphp.iniファイルがデフォルトで提供されます。
- 追加の設定ファイル: `/etc/frankenphp/php.d/*.ini`
- PHP拡張モジュール: `/usr/lib/frankenphp/modules/`
お使いのDockerのバージョンが23.0未満の場合、dockerignore[パターンの問題](https://github.com/moby/moby/pull/42676)によりビルドに失敗する可能性があります。以下のように`.dockerignore`にディレクトリを追加してください。
```patch
!testdata/*.php
!testdata/*.txt
+!caddy
+!internal
```
### Dockerを使用しない場合LinuxおよびmacOS
[ソースからのコンパイル手順](https://frankenphp.dev/docs/compile/)に従い、`--debug`設定フラグを渡してください。
## テストスイートの実行
```console
go test -tags watcher -race -v ./...
```
## Caddyモジュール
FrankenPHPのCaddyモジュール付きでCaddyをビルドします
```console
cd caddy/frankenphp/
go build -tags watcher,brotli,nobadger,nomysql,nopgx
cd ../../
```
FrankenPHPのCaddyモジュール付きでCaddyを実行します
```console
cd testdata/
../caddy/frankenphp/frankenphp run
```
サーバーは`127.0.0.1:80`で待ち受けています:
> [!NOTE]
> Dockerを使用している場合は、コンテナのポート80をバインドするか、コンテナ内で実行する必要があります。
```console
curl -vk http://127.0.0.1/phpinfo.php
```
## 最小構成のテストサーバー
最小構成のテストサーバーをビルドします:
```console
cd internal/testserver/
go build
cd ../../
```
テストサーバーを実行します:
```console
cd testdata/
../internal/testserver/testserver
```
サーバーは`127.0.0.1:8080`で待ち受けています:
```console
curl -v http://127.0.0.1:8080/phpinfo.php
```
## Dockerイメージをローカルでビルドする
bakeプランを出力します
```console
docker buildx bake -f docker-bake.hcl --print
```
amd64用のFrankenPHPイメージをローカルでビルドします
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64"
```
arm64用のFrankenPHPイメージをローカルでビルドします
```console
docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64"
```
arm64とamd64用のFrankenPHPイメージをスクラッチからビルドしてDocker Hubにプッシュします
```console
docker buildx bake -f docker-bake.hcl --pull --no-cache --push
```
## 静的ビルドでのセグメンテーション違反のデバッグ
1. GitHubからFrankenPHPバイナリのデバッグ版をダウンロードするか、デバッグシンボルを含む独自の静的ビルドを作成します
```console
docker buildx bake \
--load \
--set static-builder.args.DEBUG_SYMBOLS=1 \
--set "static-builder.platform=linux/amd64" \
static-builder
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp
```
2. 現在使用している`frankenphp`を、デバッグ版のFrankenPHP実行ファイルに置き換えます
3. 通常通りFrankenPHPを起動しますあるいは、GDBで直接FrankenPHPを開始することもできます`gdb --args frankenphp run`
4. GDBでプロセスにアタッチします
```console
gdb -p `pidof frankenphp`
```
5. 必要に応じて、GDBシェルで`continue`と入力します
6. FrankenPHPをクラッシュさせます
7. GDBシェルで`bt`と入力します
8. 出力結果をコピーします
## GitHub Actionsでのセグメンテーション違反のデバッグ
1. `.github/workflows/tests.yml`を開きます
2. PHPデバッグシンボルを有効にします
```patch
- uses: shivammathur/setup-php@v2
# ...
env:
phpts: ts
+ debug: true
```
3. `tmate`を有効にしてコンテナに接続できるようにします
```patch
- name: Set CGO flags
run: echo "CGO_CFLAGS=$(php-config --includes)" >> "$GITHUB_ENV"
+ - run: |
+ sudo apt install gdb
+ mkdir -p /home/runner/.config/gdb/
+ printf "set auto-load safe-path /\nhandle SIG34 nostop noprint pass" > /home/runner/.config/gdb/gdbinit
+ - uses: mxschmitt/action-tmate@v3
```
4. コンテナに接続します
5. `frankenphp.go`を開きます
6. `cgosymbolizer`を有効にします
```patch
- //_ "github.com/ianlancetaylor/cgosymbolizer"
+ _ "github.com/ianlancetaylor/cgosymbolizer"
```
7. モジュールを取得します:`go get`
8. コンテナ内で、GDBなどを使用できます
```console
go test -tags watcher -c -ldflags=-w
gdb --args frankenphp.test -test.run ^MyTest$
```
9. バグが修正されたら、これらの変更をすべて元に戻します
## その他の開発リソース
- [uWSGIでのPHP埋め込み](https://github.com/unbit/uwsgi/blob/master/plugins/php/php_plugin.c)
- [NGINX UnitでのPHP埋め込み](https://github.com/nginx/unit/blob/master/src/nxt_php_sapi.c)
- [Go言語でのPHP埋め込み (go-php)](https://github.com/deuill/go-php)
- [Go言語でのPHP埋め込み (GoEmPHP)](https://github.com/mikespook/goemphp)
- [C++でのPHP埋め込み](https://gist.github.com/paresy/3cbd4c6a469511ac7479aa0e7c42fea7)
- [Sara Golemon 著『Extending and Embedding PHP』](https://books.google.fr/books?id=zMbGvK17_tYC&pg=PA254&lpg=PA254#v=onepage&q&f=false)
- [TSRMLS_CCとは何か](http://blog.golemon.com/2006/06/what-heck-is-tsrmlscc-anyway.html)
- [SDL バインディング](https://pkg.go.dev/github.com/veandco/go-sdl2@v0.4.21/sdl#Main)
## Docker関連リソース
- [Bakeファイル定義](https://docs.docker.com/build/customize/bake/file-definition/)
- [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/)
## 便利なコマンド
```console
apk add strace util-linux gdb
strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1
```
## ドキュメントの翻訳
新しい言語でドキュメントとサイトを翻訳するには、
以下の手順で行ってください。
1. このリポジトリの`docs/`ディレクトリに、言語の2文字ISOコードを名前にした新しいディレクトリを作成します
2. `docs/`ディレクトリのルートにある全ての`.md`ファイルを新しいディレクトリにコピーします(翻訳のソースとして常に英語版を使用してください。英語版が最新版だからです)
3. ルートディレクトリから`README.md`と`CONTRIBUTING.md`ファイルを新しいディレクトリにコピーします
4. ファイルの内容を翻訳しますが、ファイル名は変更せず、`> [!`で始まる文字列も翻訳しないでくださいこれはGitHub用の特別なマークアップです
5. 翻訳でプルリクエストを作成します
6. [サイトリポジトリ](https://github.com/dunglas/frankenphp-website/tree/main)で、`content/``data/``i18n/`ディレクトリの翻訳ファイルをコピーして翻訳します
7. 作成されたYAMLファイルの値を翻訳します
8. サイトリポジトリでプルリクエストを開きます

110
docs/ja/README.md Normal file
View File

@@ -0,0 +1,110 @@
# FrankenPHP: PHPのためのモダンなアプリケーションサーバー
<h1 align="center"><a href="https://frankenphp.dev"><img src="frankenphp.png" alt="FrankenPHP" width="600"></a></h1>
FrankenPHPは、[Caddy](https://caddyserver.com/) Webサーバーをベースに構築された、PHPのためのモダンなアプリケーションサーバーです。
FrankenPHPは、[_Early Hints_](https://frankenphp.dev/docs/early-hints/)、[ワーカーモード](https://frankenphp.dev/docs/worker/)、[リアルタイム機能](https://frankenphp.dev/docs/mercure/)、自動HTTPS、HTTP/2、HTTP/3などの驚異的な機能により、あなたのPHPアプリに強力な力を与えます。
FrankenPHPはあらゆるPHPアプリと連携し、ワーカーモードの公式統合によってLaravelやSymfonyプロジェクトをこれまで以上に高速化します。
また、FrankenPHPはスタンドアロンのGoライブラリとしても利用可能で、`net/http`を使って任意のアプリにPHPを埋め込むことができます。
[**詳しくは** _frankenphp.dev_](https://frankenphp.dev)と、このスライド資料もご参照ください:
<a href="https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go/"><img src="https://dunglas.dev/wp-content/uploads/2022/10/frankenphp.png" alt="Slides" width="600"></a>
## はじめに
### スタンドアロンバイナリ
LinuxとmacOS向けに、[PHP 8.4](https://www.php.net/releases/8.4/en.php)と人気のPHP拡張モジュールを含む静的な
FrankenPHPバイナリを提供しています。
Windowsをお使いの場合は、[WSL](https://learn.microsoft.com/windows/wsl/)を使用してFrankenPHPを実行してください。
[FrankenPHPをダウンロード](https://github.com/php/frankenphp/releases)するか、以下のコマンドを
ターミナルにコピーして実行すると、環境に合ったバージョンが自動的にインストールされます:
```console
curl https://frankenphp.dev/install.sh | sh
mv frankenphp /usr/local/bin/
```
現在のディレクトリのコンテンツを配信するには、以下を実行してください:
```console
frankenphp php-server
```
コマンドラインスクリプトも実行できます:
```console
frankenphp php-cli /path/to/your/script.php
```
### Docker
また、[Dockerイメージ](https://frankenphp.dev/docs/docker/)も利用可能です:
```console
docker run -v .:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
ブラウザで`https://localhost`にアクセスして、FrankenPHPをお楽しみください
> [!TIP]
>
> `https://127.0.0.1`ではなく、`https://localhost`を使用して、自己署名証明書を受け入れてください。
> 使用するドメインを変更したい場合は、[`SERVER_NAME` 環境変数](docs/config.md#environment-variables)を設定してください。
### Homebrew
FrankenPHPはmacOSおよびLinux向けに[Homebrew](https://brew.sh)パッケージとしても利用可能です。
インストール方法:
```console
brew install dunglas/frankenphp/frankenphp
```
現在のディレクトリのコンテンツを配信するには、以下を実行してください:
```console
frankenphp php-server
```
## ドキュメント
- [クラシックモード](https://frankenphp.dev/docs/classic/)
- [ワーカーモード](https://frankenphp.dev/docs/worker/)
- [Early Hintsサポート103 HTTPステータスコード](https://frankenphp.dev/docs/early-hints/)
- [リアルタイム](https://frankenphp.dev/docs/mercure/)
- [大きな静的ファイルの効率的な提供](https://frankenphp.dev/docs/x-sendfile/)
- [設定](https://frankenphp.dev/docs/config/)
- [Dockerイメージ](https://frankenphp.dev/docs/docker/)
- [本番環境でのデプロイ](https://frankenphp.dev/docs/production/)
- [パフォーマンス最適化](https://frankenphp.dev/docs/performance/)
- [**スタンドアロン**、自己実行可能なPHPアプリの作成](https://frankenphp.dev/docs/embed/)
- [静的バイナリの作成](https://frankenphp.dev/docs/static/)
- [ソースからのコンパイル](https://frankenphp.dev/docs/compile/)
- [FrankenPHPの監視](https://frankenphp.dev/docs/metrics/)
- [Laravel統合](https://frankenphp.dev/docs/laravel/)
- [既知の問題](https://frankenphp.dev/docs/known-issues/)
- [デモアプリSymfonyとベンチマーク](https://github.com/dunglas/frankenphp-demo)
- [Goライブラリドキュメント](https://pkg.go.dev/github.com/dunglas/frankenphp)
- [コントリビューションとデバッグ](https://frankenphp.dev/docs/contributing/)
## 例とスケルトン
- [Symfony](https://github.com/dunglas/symfony-docker)
- [API Platform](https://api-platform.com/docs/symfony)
- [Laravel](https://frankenphp.dev/docs/laravel/)
- [Sulu](https://sulu.io/blog/running-sulu-with-frankenphp)
- [WordPress](https://github.com/StephenMiracle/frankenwp)
- [Drupal](https://github.com/dunglas/frankenphp-drupal)
- [Joomla](https://github.com/alexandreelise/frankenphp-joomla)
- [TYPO3](https://github.com/ochorocho/franken-typo3)
- [Magento2](https://github.com/ekino/frankenphp-magento2)

11
docs/ja/classic.md Normal file
View File

@@ -0,0 +1,11 @@
# クラシックモードの使用
追加の設定を行わなくても、FrankenPHPはクラシックモードで動作します。このモードでは、FrankenPHPは従来のPHPサーバーのように機能し、PHPファイルを直接提供します。これにより、PHP-FPMやmod_phpを使ったApacheの置き換えとしてシームレスに利用できます。
Caddyと同様に、FrankenPHPは無制限の接続を受け付け、[固定数のスレッド](config.md#caddyfile-config)でそれらを処理します。受け入れられキューに入れられる接続の数は、利用可能なシステムリソースによってのみ制限されます。
PHPスレッドプールは、起動時に初期化された固定数のスレッドで動作し、これはPHP-FPMの静的モードに相当します。また、PHP-FPMの動的モードと同様に、[実行時にスレッドを自動的にスケール](performance.md#max_threads)させることも可能です。
キューに入った接続は、PHPスレッドが空くまで無期限に待機します。これを避けるために、FrankenPHP のグローバル設定内の `max_wait_time` [設定](config.md#caddyfile-config)を使って、リクエストが空きスレッドを待てる最大時間を制限し、それを超えるとリクエストが拒否されるようにできます。
加えて、[Caddy側で適切な書き込みタイムアウト](https://caddyserver.com/docs/caddyfile/options#timeouts)を設定することも可能です。
各Caddyインスタンスは、1つのFrankenPHPスレッドプールのみを起動し、すべての`php_server`ブロック間でこのプールを共有します。

127
docs/ja/compile.md Normal file
View File

@@ -0,0 +1,127 @@
# ソースからのコンパイル
このドキュメントでは、PHPを動的ライブラリとしてロードするFrankenPHPバイナリの作成方法を説明します。
これが推奨される方法です。
または、[完全静的およびほぼ静的なビルド](static.md)も作成できます。
## PHPのインストール
FrankenPHPはPHP 8.2以上と互換性があります。
### Homebrewを使用する場合LinuxとMac
FrankenPHPと互換性のあるlibphpのバージョンをインストールする最も簡単な方法は、[Homebrew PHP](https://github.com/shivammathur/homebrew-php)が提供するZTSパッケージを使用することです。
まず、まだインストールしていない場合は[Homebrew](https://brew.sh)をインストールしてください。
次に、PHPのZTSバリアント、Brotliオプション、圧縮サポート用、watcherオプション、ファイル変更検出用をインストールします
```console
brew install shivammathur/php/php-zts brotli watcher
brew link --overwrite --force shivammathur/php/php-zts
```
### PHPをコンパイルする場合
別の方法として、FrankenPHPに必要なオプションを指定してPHPをソースからコンパイルすることもできます。
まず、[PHPのソース](https://www.php.net/downloads.php)を取得して展開します:
```console
tar xf php-*
cd php-*/
```
次に、プラットフォームに応じて必要なオプションを指定して`configure`スクリプトを実行します。
以下の`./configure`フラグは必須ですが、例えば拡張機能モジュールや追加機能をコンパイルするために他のフラグを追加することもできます。
#### Linux
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--enable-zend-max-execution-timers
```
#### Mac
[Homebrew](https://brew.sh/)パッケージマネージャーを使用して、必須およびオプションの依存関係をインストールします:
```console
brew install libiconv bison brotli re2c pkg-config watcher
echo 'export PATH="/opt/homebrew/opt/bison/bin:$PATH"' >> ~/.zshrc
```
その後、以下のようにconfigureスクリプトを実行します
```console
./configure \
--enable-embed \
--enable-zts \
--disable-zend-signals \
--with-iconv=/opt/homebrew/opt/libiconv/
```
#### PHPのコンパイル
最後に、PHPをコンパイルしてインストールします
```console
make -j"$(getconf _NPROCESSORS_ONLN)"
sudo make install
```
## オプション依存関係のインストール
FrankenPHPの一部の機能は、システムにインストールされているオプションの依存パッケージに依存しています。
または、Goコンパイラにビルドタグを渡すことで、これらの機能を無効にできます。
| 機能 | 依存関係 | 無効にするためのビルドタグ |
|--------------------------------|-----------------------------------------------------------------------|-------------------------|
| Brotli圧縮 | [Brotli](https://github.com/google/brotli) | nobrotli |
| ファイル変更時のワーカー再起動 | [Watcher C](https://github.com/e-dant/watcher/tree/release/watcher-c) | nowatcher |
## Goアプリのコンパイル
いよいよ最終的なバイナリをビルドできるようになりました。
### xcaddyを使う場合
推奨される方法は、[xcaddy](https://github.com/caddyserver/xcaddy)を使用してFrankenPHPをコンパイルする方法です。
`xcaddy`を使うと、[Caddyのカスタムモジュール](https://caddyserver.com/docs/modules/)やFrankenPHP拡張を簡単に追加できます
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with github.com/dunglas/frankenphp/caddy \
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# 追加のCaddyモジュールとFrankenPHP拡張をここに追加
```
> [!TIP]
>
> musl libcAlpine LinuxのデフォルトとSymfonyを使用している場合、
> デフォルトのスタックサイズを増やす必要がある場合があります。
> そうしないと、`PHP Fatal error: Maximum call stack size of 83360 bytes reached during compilation. Try splitting expression`のようなエラーが発生する可能性があります。
>
> これを行うには、`XCADDY_GO_BUILD_FLAGS`環境変数を
> `XCADDY_GO_BUILD_FLAGS=$'-ldflags "-w -s -extldflags \'-Wl,-z,stack-size=0x80000\'"'`のようなものに変更してください
> (アプリの要件に応じてスタックサイズの値を変更してください)。
### xcaddyを使用しない場合
代替として、`xcaddy`を使わずに`go`コマンドを直接使ってFrankenPHPをコンパイルすることも可能です
```console
curl -L https://github.com/php/frankenphp/archive/refs/heads/main.tar.gz | tar xz
cd frankenphp-main/caddy/frankenphp
CGO_CFLAGS=$(php-config --includes) CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" go build -tags=nobadger,nomysql,nopgx
```

306
docs/ja/config.md Normal file
View File

@@ -0,0 +1,306 @@
# 設定
FrankenPHP、Caddy、そしてMercureやVulcainモジュールは、[Caddyでサポートされる形式](https://caddyserver.com/docs/getting-started#your-first-config)を使用して設定できます。
[Dockerイメージ](docker.md)では、`Caddyfile``/etc/frankenphp/Caddyfile`に配置されています。
静的バイナリは、`frankenphp run`コマンドを実行したディレクトリ内の`Caddyfile`を参照します。
また、`-c`または`--config`オプションでカスタムのパスを指定できます。
PHP自体の設定は[`php.ini` ファイルを使用](https://www.php.net/manual/en/configuration.file.php)して行えます。
インストール方法に応じて、PHPインタープリターは上記いずれかの場所にある設定ファイルを参照します。
## Docker
- `php.ini`: `/usr/local/etc/php/php.ini`(デフォルトでは`php.ini`は含まれていません)
- 追加の設定ファイル: `/usr/local/etc/php/conf.d/*.ini`
- PHP拡張モジュール: `/usr/local/lib/php/extensions/no-debug-zts-<YYYYMMDD>/`
- PHPプロジェクトが提供する公式テンプレートをコピーすることを推奨します
```dockerfile
FROM dunglas/frankenphp
# 本番環境:
RUN cp $PHP_INI_DIR/php.ini-production $PHP_INI_DIR/php.ini
# または開発環境:
RUN cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini
```
## RPMおよびDebianパッケージ
- `php.ini`: `/etc/frankenphp/php.ini`(本番環境向けのプリセットの`php.ini`ファイルがデフォルトで提供されます)
- 追加の設定ファイル: `/etc/frankenphp/php.d/*.ini`
- PHP拡張モジュール: `/usr/lib/frankenphp/modules/`
## 静的バイナリ
- `php.ini`: `frankenphp run`または`frankenphp php-server`を実行したディレクトリ内、なければ`/etc/frankenphp/php.ini`を参照
- 追加の設定ファイル: `/etc/frankenphp/php.d/*.ini`
- PHP拡張モジュール: ロードできません、バイナリ自体にバンドルする必要があります
- [PHPソース](https://github.com/php/php-src/)で提供される`php.ini-production`または`php.ini-development`のいずれかをコピーしてください
## Caddyfileの設定
`php_server`または`php`の[HTTPディレクティブ](https://caddyserver.com/docs/caddyfile/concepts#directives)は、サイトブロック内で使用してPHPアプリを配信できます。
最小構成の例:
```caddyfile
localhost {
# 圧縮を有効化(オプション)
encode zstd br gzip
# 現在のディレクトリ内のPHPファイルを実行し、アセットを配信
php_server
}
```
グローバルオプションを使用してFrankenPHPを明示的に設定することもできます
`frankenphp`の[グローバルオプション](https://caddyserver.com/docs/caddyfile/concepts#global-options)を使用してFrankenPHPを構成できます。
```caddyfile
{
frankenphp {
num_threads <num_threads> # 開始するPHPスレッド数を設定します。デフォルト: 利用可能なCPU数の2倍。
max_threads <num_threads> # 実行時に追加で開始できるPHPスレッドの最大数を制限します。デフォルト: num_threads。 'auto'を設定可能。
max_wait_time <duration> # リクエストがタイムアウトする前にPHPのスレッドが空くのを待つ最大時間を設定します。デフォルト: 無効。
php_ini <key> <value> # php.iniのディレクティブを設定します。複数のディレクティブを設定するために何度でも使用できます。
worker {
file <path> # ワーカースクリプトのパスを設定します。
num <num> # 開始するPHPスレッド数を設定します。デフォルト: 利用可能なCPU数の2倍。
env <key> <value> # 追加の環境変数を指定された値に設定する。複数の環境変数に対して複数回指定することができます。
watch <path> # ファイル変更を監視するパスを設定します。複数のパスに対して複数回指定できます。
name <name> # ワーカーの名前を設定します。ログとメトリクスで使用されます。デフォルト: ワーカーファイルの絶対パス
max_consecutive_failures <num> # workerが不健全とみなされるまでの、連続失敗の最大回数を設定します。 -1 はワーカーを常に再起動することを意味します。デフォルトは 6 です。
}
}
}
# ...
```
代わりに、`worker`オプションのワンライナー形式を使用することもできます:
```caddyfile
{
frankenphp {
worker <file> <num>
}
}
# ...
```
同じサーバーで複数のアプリを提供する場合は、複数のワーカーを定義することもできます:
```caddyfile
app.example.com {
root /path/to/app/public
php_server {
root /path/to/app/public # キャッシュ効率を高める
worker index.php <num>
}
}
other.example.com {
root /path/to/other/public
php_server {
root /path/to/other/public
worker index.php <num>
}
}
# ...
```
通常は`php_server`ディレクティブを使えば十分ですが、
より細かい制御が必要な場合は、より低レベルの`php`ディレクティブを使用できます。
`php`ディレクティブは、対象がPHPファイルかどうかを確認せず、すべての入力をPHPに渡します。
詳しくは[パフォーマンスページ](performance.md#try_files)をお読みください。
`php_server`ディレクティブの使用は、以下の設定と同等です:
```caddyfile
route {
# ディレクトリへのリクエストに末尾スラッシュを追加
@canonicalPath {
file {path}/index.php
not path */
}
redir @canonicalPath {path}/ 308
# 要求されたファイルが存在しない場合は、indexファイルを試行
@indexFiles file {
try_files {path} {path}/index.php index.php
split_path .php
}
rewrite @indexFiles {http.matchers.file.relative}
# FrankenPHP!
@phpFiles path *.php
php @phpFiles
file_server
}
```
`php_server``php`ディレクティブには以下のオプションがあります:
```caddyfile
php_server [<matcher>] {
root <directory> # サイトのルートフォルダを設定します。デフォルト: `root`ディレクティブ。
split_path <delim...> # URIを2つの部分に分割するための部分文字列を設定します。最初にマッチする部分文字列がURIから「パス情報」を分割するために使用されます。最初の部分はマッチする部分文字列で接尾辞が付けられ、実際のリソースCGIスクリプト名とみなされます。2番目の部分はスクリプトが使用する PATH_INFO に設定されます。デフォルト: `.php`
resolve_root_symlink false # シンボリックリンクが存在する場合`root`ディレクトリをシンボリックリンクの評価によって実際の値に解決することを無効にする(デフォルトで有効)。
env <key> <value> # 追加の環境変数を指定された値に設定する。複数の環境変数を指定する場合は、複数回指定することができます。
file_server off # 組み込みのfile_serverディレクティブを無効にします。
worker { # このサーバー固有のワーカーを作成します。複数のワーカーに対して複数回指定できます。
file <path> # ワーカースクリプトへのパスを設定します。php_serverのルートからの相対パスとなります。
num <num> # 起動するPHPスレッド数を設定します。デフォルトは利用可能なスレッド数の 2 倍です。
name <name> # ログとメトリクスで使用されるワーカーの名前を設定します。デフォルト: ワーカーファイルの絶対パス。php_server ブロックで定義されている場合は、常にm#で始まります。
watch <path> # ファイルの変更を監視するパスを設定する。複数のパスに対して複数回指定することができる。
env <key> <value> # 追加の環境変数を指定された値に設定する。複数の環境変数を指定する場合は、複数回指定することができます。このワーカーの環境変数もphp_serverの親から継承されますが、 ここで上書きすることもできます。
match <path> # ワーカーをパスパターンにマッチさせます。try_filesを上書きし、php_serverディレクティブでのみ使用できます。
}
worker <other_file> <num> # グローバルfrankenphpブロックのような短縮形式も使用できます。
}
```
### ファイルの変更監視
ワーカーはアプリケーションを一度だけ起動してメモリに保持するため、
PHPファイルに変更を加えても即座には反映されません。
代わりに、`watch`ディレクティブを使用してファイル変更時にワーカーを再起動させることができます。
これは開発環境において有用です。
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch
}
}
}
```
`watch`ディレクトリが指定されていない場合、`./**/*.{php,yaml,yml,twig,env}`にフォールバックします。
これは、FrankenPHPプロセスが開始されたディレクトリおよびそのサブディレクトリ内のすべての`.php``.yaml``.yml``.twig``.env`ファイルすべてを監視します。
代わりに、[シェルのファイル名パターン](https://pkg.go.dev/path/filepath#Match)を使用して
1つ以上のディレクトリを指定することもできます
```caddyfile
{
frankenphp {
worker {
file /path/to/app/public/worker.php
watch /path/to/app # /path/to/app以下すべてのサブディレクトリのファイルを監視
watch /path/to/app/*.php # /path/to/app内の.phpで終わるファイルを監視
watch /path/to/app/**/*.php # /path/to/appおよびサブディレクトリのPHPファイルを監視
watch /path/to/app/**/*.{php,twig} # /path/to/appおよびサブディレクトリ内のPHPとTwigファイルを監視
}
}
}
```
- `**` パターンは再帰的な監視を意味します
- ディレクトリは相対パスFrankenPHPプロセスの開始ディレクトリからにもできます
- 複数のワーカーが定義されている場合、いずれかのファイルが変更されるとすべてのワーカーが再起動されます
- 実行時に生成されるファイル(ログなど)を監視対象に含めると、意図しないワーカーの再起動を引き起こす可能性があるため注意が必要です
ファイルウォッチャーは[e-dant/watcher](https://github.com/e-dant/watcher)に基づいています。
## パスにワーカーをマッチさせる
従来のPHPアプリケーションでは、スクリプトは常にpublicディレクトリに配置されます。
これはワーカースクリプトにも当てはまり、他のPHPスクリプトと同様に扱われます。
ワーカースクリプトをpublicディレクトリの外に配置したい場合は、`match`ディレクティブを使用して実現できます。
`match`ディレクティブは、`try_files`の最適化された代替手段であり、`php_server`および`php`の中でのみ使用できます。
次の例では、public ディレクトリ内にファイルが存在すればそれを配信し、
存在しなければ、パスパターンに一致するワーカーにリクエストを転送します。
```caddyfile
{
frankenphp {
php_server {
worker {
file /path/to/worker.php # ファイルはpublicパス外でも可
match /api/* # /api/で始まるすべてのリクエストはこのワーカーで処理される
}
}
}
}
```
### フルデュプレックスHTTP/1
HTTP/1.xを使用する場合、全体のボディが読み取られる前にレスポンスを書き込めるようにするため、
フルデュプレックスモードを有効にすることが望ましい場合がありますWebSocket、Server-Sent Eventsなど
これは明示的に有効化する必要がある設定で、`Caddyfile`のグローバルオプションに追加する必要があります:
```caddyfile
{
servers {
enable_full_duplex
}
}
```
> [!CAUTION]
>
> このオプションを有効にすると、フルデュプレックスをサポートしない古いHTTP/1.xクライアントでデッドロックが発生する可能性があります。
> これは`CADDY_GLOBAL_OPTIONS`環境設定を使用しても設定できます:
```sh
CADDY_GLOBAL_OPTIONS="servers {
enable_full_duplex
}"
```
この設定の詳細については、[Caddyドキュメント](https://caddyserver.com/docs/caddyfile/options#enable-full-duplex)をご覧ください。
## 環境変数
以下の環境変数を使用することで、`Caddyfile`を直接変更せずにCaddyディレクティブを注入できます
- `SERVER_NAME`: [待ち受けアドレス](https://caddyserver.com/docs/caddyfile/concepts#addresses)を変更し、指定したホスト名はTLS証明書の生成にも使用されます
- `SERVER_ROOT`: サイトのルートディレクトリを変更します。デフォルトは`public/`
- `CADDY_GLOBAL_OPTIONS`: [グローバルオプション](https://caddyserver.com/docs/caddyfile/options)を注入します
- `FRANKENPHP_CONFIG`: `frankenphp`ディレクティブの下に設定を注入します
FPM や CLI SAPI と同様に、環境変数はデフォルトで`$_SERVER`スーパーグローバルで公開されます。
[`variables_order` PHPディレクティブ](https://www.php.net/manual/en/ini.core.php#ini.variables-order)の`S`値は、このディレクティブ内での`E`の位置にかかわらず常に`ES`と同等です。
## PHP設定
[追加のPHP設定ファイル](https://www.php.net/manual/en/configuration.file.php#configuration.file.scan)を読み込むには、
`PHP_INI_SCAN_DIR`環境変数を使用できます。
設定されると、PHPは指定されたディレクトリに存在する`.ini`拡張子を持つすべてのファイルを読み込みます。
また、`Caddyfile``php_ini`ディレクティブを使用してPHP設定を変更することもできます
```caddyfile
{
frankenphp {
php_ini memory_limit 256M
# または
php_ini {
memory_limit 256M
max_execution_time 15
}
}
}
```
## デバッグモードの有効化
Dockerイメージを使用する場合、`CADDY_GLOBAL_OPTIONS`環境変数に`debug`を設定するとデバッグモードが有効になります:
```console
docker run -v $PWD:/app/public \
-e CADDY_GLOBAL_OPTIONS=debug \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```

205
docs/ja/docker.md Normal file
View File

@@ -0,0 +1,205 @@
# カスタムDockerイメージのビルド
[FrankenPHPのDockerイメージ](https://hub.docker.com/r/dunglas/frankenphp)は、[公式PHPイメージ](https://hub.docker.com/_/php/)をベースにしています。主要なアーキテクチャに対してDebianとAlpine Linuxのバリアントを提供しており、Debianバリアントの使用を推奨しています。
PHP 8.2、8.3、8.4向けのバリアントが提供されています。
タグは次のパターンに従います:`dunglas/frankenphp:<frankenphp-version>-php<php-version>-<os>`
- `<frankenphp-version>`および`<php-version>`は、それぞれFrankenPHPおよびPHPのバージョン番号で、メジャー`1`)、マイナー(例:`1.2`)からパッチバージョン(例:`1.2.3`)まであります。
- `<os>``bookworm`Debian Bookworm用または`alpine`Alpine最新安定版用のいずれかです。
[タグを閲覧](https://hub.docker.com/r/dunglas/frankenphp/tags)。
## イメージの使用方法
プロジェクトに`Dockerfile`を作成します:
```dockerfile
FROM dunglas/frankenphp
COPY . /app/public
```
次に、以下のコマンドを実行してDockerイメージをビルドし、実行します
```console
docker build -t my-php-app .
docker run -it --rm --name my-running-app my-php-app
```
## PHP拡張モジュールの追加インストール方法
ベースイメージには[`docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer)スクリプトが含まれており、
追加のPHP拡張モジュールを簡単にインストールできます
```dockerfile
FROM dunglas/frankenphp
# 追加の拡張モジュールをここに追加:
RUN install-php-extensions \
pdo_mysql \
gd \
intl \
zip \
opcache
```
## Caddyモジュールの追加インストール方法
FrankenPHPはCaddyをベースに構築されているため、すべての[Caddyモジュール](https://caddyserver.com/docs/modules/)をFrankenPHPでも使用できます。
カスタムCaddyモジュールをインストールする最も簡単な方法は、[xcaddy](https://github.com/caddyserver/xcaddy)を使用することです:
```dockerfile
FROM dunglas/frankenphp:builder AS builder
# builderイメージにxcaddyをコピー
COPY --from=caddy:builder /usr/bin/xcaddy /usr/bin/xcaddy
# FrankenPHPをビルドするにはCGOを有効にする必要があります
RUN CGO_ENABLED=1 \
XCADDY_SETCAP=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output /usr/local/bin/frankenphp \
--with github.com/dunglas/frankenphp=./ \
--with github.com/dunglas/frankenphp/caddy=./caddy/ \
--with github.com/dunglas/caddy-cbrotli \
# MercureとVulcainは公式ビルドに含まれていますが、お気軽に削除してください
--with github.com/dunglas/mercure/caddy \
--with github.com/dunglas/vulcain/caddy
# ここに追加のCaddyモジュールを指定してください
FROM dunglas/frankenphp AS runner
# 公式バイナリをカスタムモジュールを含むものに置き換え
COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp
```
FrankenPHPが提供する`builder`イメージには、コンパイル済みの`libphp`が含まれています。
[ビルダーイメージ](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder)は、FrankenPHPおよびPHPのすべてのバージョンに対して、DebianとAlpineの両方が提供されています。
> [!TIP]
>
> Alpine LinuxとSymfonyを使用している場合は、
> [デフォルトのスタックサイズを増やす](compile.md#using-xcaddy) 必要がある場合があります。
## デフォルトでワーカーモードを有効にする
FrankenPHPをワーカースクリプトで起動するには、`FRANKENPHP_CONFIG`環境変数を設定します:
```dockerfile
FROM dunglas/frankenphp
# ...
ENV FRANKENPHP_CONFIG="worker ./public/index.php"
```
## 開発時にボリュームを使う
FrankenPHPでの開発を簡単に行うには、ホスト側のアプリケーションのソースコードを含むディレクトリを、Dockerコンテナ内にボリュームとしてマウントします
```console
docker run -v $PWD:/app/public -p 80:80 -p 443:443 -p 443:443/udp --tty my-php-app
```
> [!TIP]
>
> `--tty`オプションを使うと、JSONではなく人間が読みやすいログが表示されます。
Docker Composeを使用する場合
```yaml
# compose.yaml
services:
php:
image: dunglas/frankenphp
# カスタムDockerfileを使用したい場合は以下の行のコメントを外してください
#build: .
# 本番環境で使用する場合は以下の行のコメントを外してください
# restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- ./:/app/public
- caddy_data:/data
- caddy_config:/config
# 開発環境で人間が読みやすいログを出力するため、本番ではこの行をコメントアウトしてください
tty: true
# Caddyの証明書や設定に必要なボリューム
volumes:
caddy_data:
caddy_config:
```
## 非rootユーザーとして実行する
FrankenPHPはDockerで非rootユーザーとして実行できます。
これを行うサンプル`Dockerfile`は以下の通りです:
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Alpine系ディストリビューションでは "adduser -D ${USER}" を使用
useradd ${USER}; \
# ポート 80 や 443 にバインドするための追加ケーパビリティを追加
setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp; \
# /data/caddy および /config/caddy への書き込み権限を付与
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
USER ${USER}
```
### ケーパビリティなしでの実行
FrankenPHPをroot以外のユーザーで実行する場合でも、特権ポート80と443でWebサーバーを
バインドするために`CAP_NET_BIND_SERVICE`ケーパビリティが必要です。
FrankenPHPを非特権ポート1024以上で公開する場合は、
ウェブサーバーを非rootユーザーとして実行し、ケーパビリティを必要とせずに実行することが可能です
```dockerfile
FROM dunglas/frankenphp
ARG USER=appuser
RUN \
# Alpine 系ディストリビューションでは "adduser -D ${USER}" を使用
useradd ${USER}; \
# デフォルトのケーパビリティを削除
setcap -r /usr/local/bin/frankenphp; \
# /data/caddy と /config/caddy への書き込み権限を付与
chown -R ${USER}:${USER} /data/caddy && chown -R ${USER}:${USER} /config/caddy
USER ${USER}
```
その後、`SERVER_NAME`環境変数を設定して非特権ポートを使用します。
例: `:8000`
## アップデート
Dockerイメージは以下のタイミングでビルドされます
- 新しいリリースがタグ付けされたとき
- 公式PHPイメージに新しいバージョンがある場合、毎日UTC午前4時に自動ビルド
## 開発版
開発版は[`dunglas/frankenphp-dev`](https://hub.docker.com/repository/docker/dunglas/frankenphp-dev)Dockerリポジトリで利用できます。
GitHubリポジトリのmainブランチにコミットがpushされるたびに新しいビルドが実行されます。
`latest*`タグは`main`ブランチのヘッドを指しており、
`sha-<git-commit-hash>` 形式のタグも利用可能です。

21
docs/ja/early-hints.md Normal file
View File

@@ -0,0 +1,21 @@
# Early Hints
FrankenPHPは[103 Early Hints ステータスコード](https://developer.chrome.com/blog/early-hints/)をネイティブサポートしています。
Early Hintsを使用することで、ウェブページの読み込み時間を30%改善できます。
```php
<?php
header('Link: </style.css>; rel=preload; as=style');
headers_send(103);
// 遅いアルゴリズムとSQLクエリ 🤪
echo <<<'HTML'
<!DOCTYPE html>
<title>Hello FrankenPHP</title>
<link rel="stylesheet" href="style.css">
HTML;
```
Early Hintsは通常モードと[ワーカー](worker.md)モードの両方でサポートされています。

143
docs/ja/embed.md Normal file
View File

@@ -0,0 +1,143 @@
# PHPアプリのスタンドアロンバイナリ化
FrankenPHPには、PHPアプリケーションのソースコードやアセットを静的な自己完結型バイナリに埋め込む機能があります。
この機能により、PHPアプリケーション自体に加えて、PHPインタープリターや本番環境対応のWebサーバーCaddyも含んだスタンドアロンバイナリとして配布できます。
この機能について詳しくは、[SymfonyCon 2023でKévinが行ったプレゼンテーション](https://dunglas.dev/2023/12/php-and-symfony-apps-as-standalone-binaries/)をご覧ください。
Laravelアプリケーションの埋め込みについては、[こちらの専用ドキュメント](laravel.md#laravel-apps-as-standalone-binaries)をお読みください。
## アプリの準備
自己完結型バイナリを作成する前に、アプリが埋め込みに対応できる状態にあることを確認してください。
例えば、以下のような作業が必要です:
- 本番環境用の依存パッケージをインストールする
- オートローダーをダンプする
- アプリケーションの本番モードを有効にする(ある場合)
- 最終バイナリのサイズを減らすために`.git`やテストなどの不要なファイルを除外する
例えば、Symfonyアプリの場合、以下のコマンドを使用できます
```console
# .git/ などを除去するためにプロジェクトをエクスポート
mkdir $TMPDIR/my-prepared-app
git archive HEAD | tar -x -C $TMPDIR/my-prepared-app
cd $TMPDIR/my-prepared-app
# 適切な環境変数を設定
echo APP_ENV=prod > .env.local
echo APP_DEBUG=0 >> .env.local
# テストやその他不要ファイルを削除して容量削減
# あるいは、 .gitattributes の export-ignore 属性にこれらを追加してもよい
rm -Rf tests/
# 依存パッケージをインストール
composer install --ignore-platform-reqs --no-dev -a
# .env を最適化
composer dump-env prod
```
### 設定のカスタマイズ
[設定](config.md) をカスタマイズするには、埋め込まれるアプリのメインディレクトリ
(前の例では`$TMPDIR/my-prepared-app`)に`Caddyfile``php.ini`ファイルを配置できます。
## Linux用バイナリの作成
Linux用バイナリを作成する最も簡単な方法は、提供されているDockerベースのビルダーを使用することです。
1. アプリのリポジトリに`static-build.Dockerfile`というファイルを作成します:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# アプリをコピー
WORKDIR /go/src/app/dist/app
COPY . .
# 静的バイナリをビルド
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> 一部の`.dockerignore`ファイル(例:デフォルトの[Symfony Docker `.dockerignore`](https://github.com/dunglas/symfony-docker/blob/main/.dockerignore)
> は`vendor/`ディレクトリと`.env`ファイルを無視します。ビルド前に`.dockerignore`ファイルを調整または削除してください。
2. ビルドします:
```console
docker build -t static-app -f static-build.Dockerfile .
```
3. バイナリを抽出します:
```console
docker cp $(docker create --name static-app-tmp static-app):/go/src/app/dist/frankenphp-linux-x86_64 my-app ; docker rm static-app-tmp
```
生成されるバイナリは、現在のディレクトリの`my-app`というファイル名になります。
## 他のOS用のバイナリの作成
Dockerを使用したくない場合や、macOSバイナリを作成したい場合は、提供されているシェルスクリプトを使用してください
```console
git clone https://github.com/php/frankenphp
cd frankenphp
EMBED=/path/to/your/app ./build-static.sh
```
生成されるバイナリは、`dist/`ディレクトリの`frankenphp-<os>-<arch>`という名前のファイルです。
## バイナリの使い方
これで完了です!`my-app`ファイルまたは他のOSでは`dist/frankenphp-<os>-<arch>`)には、自己完結型アプリが含まれています!
Webアプリを起動するには、以下を実行します
```console
./my-app php-server
```
アプリに[ワーカースクリプト](worker.md)が含まれている場合は、以下のようにワーカーを開始します:
```console
./my-app php-server --worker public/index.php
```
HTTPSLet's Encrypt証明書は自動作成、HTTP/2、HTTP/3を有効にするには、使用するドメイン名を指定してください
```console
./my-app php-server --domain localhost
```
バイナリに埋め込まれたPHP CLIスクリプトも実行できます
```console
./my-app php-cli bin/console
```
## PHP拡張モジュール
デフォルトでは、スクリプトはプロジェクトの`composer.json`ファイルで必要な拡張モジュールをビルドします(存在する場合)。
`composer.json`ファイルが存在しない場合、[静的ビルドのドキュメント](static.md)に記載されているデフォルトの拡張モジュールがビルドされます。
拡張モジュールをカスタマイズしたい場合は、`PHP_EXTENSIONS`環境変数を使用してください。
## ビルドのカスタマイズ
バイナリをカスタマイズする方法拡張モジュール、PHPバージョンなどについては、[静的ビルドのドキュメント](static.md)をお読みください。
## バイナリの配布
Linuxでは、作成されたバイナリは[UPX](https://upx.github.io)を使用して圧縮されます。
Macでは、送信前にファイルサイズを減らすために圧縮できます。
`xz`の使用をお勧めします。

639
docs/ja/extensions.md Normal file
View File

@@ -0,0 +1,639 @@
# GoでPHP拡張モジュールを作成する
FrankenPHPでは、**GoでPHP拡張モジュールを作成する**ことができます。これにより、PHPから直接呼び出せる**高パフォーマンスなネイティブ関数**を作成できます。アプリケーションは既存または新しいGoライブラリを活用でき、**PHPコードから直接goroutineの**強力な並行性モデルを使用できます。
PHP拡張モジュールの記述は通常Cで行われますが、少しの追加作業で他の言語でも作成可能です。PHP拡張モジュールは低レベル言語の力を活用してPHPの機能を拡張することができます。例えば、ネイティブ関数を追加したり、特定の操作を最適化したりできます。
Caddyモジュールのおかげで、GoでPHP拡張モジュールを書いてFrankenPHPに簡単に統合できます。
## 2つのアプローチ
FrankenPHPでは、GoでPHP拡張モジュールを作成する2つの方法を提供します
1. **拡張モジュールジェネレーターを使用** - ほとんどのユースケースに必要なボイラープレートを自動生成する推奨アプローチで、Goコードの記述に集中できます
2. **手動実装** - 拡張モジュール構造を細かく制御したい高度なユースケース
最初に始めやすいジェネレーター方式を紹介し、その後で完全な制御が必要な場合の手動実装方式を説明します。
## 拡張モジュールジェネレーターを使用する
FrankenPHPにはGoのみを使用して**PHP拡張モジュールを作成する**ツールが付属しています。**Cコードを書く必要がなく**、CGOを直接使用する必要もありません。FrankenPHPには**パブリック型API**も含まれており、**PHP/CとGo間の型変換**を心配することなくGoでPHP拡張を書くのに役立ちます。
> [!TIP]
> 拡張モジュールをGoで一から書く方法を理解したい場合は、ジェネレーターを使用せずにGoでPHP拡張モジュールを書く方法を紹介する後述の手動実装セクションを参照してください。
注意すべきことは、このツールは**完全な拡張モジュールジェネレーター**ではないことです。GoでシンプルなPHP拡張モジュールを書くのには十分役立ちますが、高度なPHP拡張モジュールの機能には対応していません。より**複雑で最適化された**拡張モジュールを書く必要がある場合は、Cコードを書いたり、CGOを直接使用したりする必要があるかもしれません。
### 前提条件
以下の手動実装セクションでも説明しているように、[PHPのソースを取得](https://www.php.net/downloads.php)し、新しいGoモジュールを作成する必要があります。
#### 新しいモジュールの作成とPHPソースの取得
GoでPHP拡張モジュールを書く最初のステップは、新しいGoモジュールの作成です。以下のコマンドを使用できます
```console
go mod init github.com/my-account/my-module
```
2番目のステップは、次のステップのために[PHPのソースを取得](https://www.php.net/downloads.php)することです。取得したら、Goモジュールのディレクトリ内ではなく、任意のディレクトリに展開します
```console
tar xf php-*
```
### 拡張モジュールの記述
これでGoでネイティブ関数を書く準備が整いました。`stringext.go`という名前の新しいファイルを作成します。最初の関数は文字列を引数として取り、それを指定された回数だけ繰り返し、文字列を逆転するかどうかを示すブール値を受け取り、結果の文字列を返します。これは以下のようになります:
```go
import (
"C"
"github.com/dunglas/frankenphp"
"strings"
)
//export_php:function repeat_this(string $str, int $count, bool $reverse): string
func repeat_this(s *C.zend_string, count int64, reverse bool) unsafe.Pointer {
str := frankenphp.GoString(unsafe.Pointer(s))
result := strings.Repeat(str, int(count))
if reverse {
runes := []rune(result)
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
runes[i], runes[j] = runes[j], runes[i]
}
result = string(runes)
}
return frankenphp.PHPString(result, false)
}
```
ここで重要なポイントが2つあります
* ディレクティブコメント`//export_php:function`はPHPでの関数シグネチャを定義します。これにより、ジェネレーターは適切なパラメータと戻り値の型でPHP関数を生成する方法を知ることができます。
* 関数は`unsafe.Pointer`を返さなければなりません。FrankenPHPはCとGo間の型変換を支援するAPIを提供しています。
前者は理解しやすいですが、後者は少し複雑かもしれません。次のセクションで型変換について詳しく説明します。
### 型変換
C/PHPとGoの間でメモリ表現が同じ変数型もありますが、直接使用するにはより多くのロジックが必要な型もあります。これは拡張モジュールを書く際の最も挑戦的な部分かもしれません。Zendエンジンの内部仕組みや、変数がPHP内でどのように格納されているかを理解する必要があるためです。以下の表は、知っておくべき重要な情報をまとめています
| PHP型 | Go型 | 直接変換 | CからGoヘルパー | GoからCヘルパー | クラスメソッドサポート |
|--------------------|------------------|-------------------|-----------------------|------------------------|-----------------------|
| `int` | `int64` | ✅ | - | - | ✅ |
| `?int` | `*int64` | ✅ | - | - | ✅ |
| `float` | `float64` | ✅ | - | - | ✅ |
| `?float` | `*float64` | ✅ | - | - | ✅ |
| `bool` | `bool` | ✅ | - | - | ✅ |
| `?bool` | `*bool` | ✅ | - | - | ✅ |
| `string`/`?string` | `*C.zend_string` | ❌ | frankenphp.GoString() | frankenphp.PHPString() | ✅ |
| `array` | `slice`/`map` | ❌ | _未実装_ | _未実装_ | ❌ |
| `object` | `struct` | ❌ | _未実装_ | _未実装_ | ❌ |
> [!NOTE]
> この表はまだ完全ではなく、FrankenPHPの型APIがより完全になるにつれて完成されます。
>
> クラスメソッドについては、現在プリミティブ型のみがサポートされています。配列とオブジェクトはまだメソッドパラメータや戻り値の型として使用できません。
前のセクションのコードスニペットを参照すると、最初のパラメータと戻り値の変換にヘルパーが使用されていることがわかります。 `repeat_this()`関数の2番目と3番目の引数は、基礎となる型のメモリ表現がCとGoで同じであるため、変換する必要がありません。
### ネイティブPHPクラスの宣言
ジェネレーターは、PHPオブジェクトを作成するために使用できる**不透明クラスopaque classes**をGo構造体として宣言することをサポートしています。`//export_php:class`ディレクティブコメントを使用してPHPクラスを定義できます。例
```go
//export_php:class User
type UserStruct struct {
Name string
Age int
}
```
#### 不透明クラスとは何ですか?
**不透明クラスopaque classes**は、内部構造プロパティがPHPコードから隠されているクラスです。これは以下を意味します
* **プロパティへの直接アクセス不可** PHPから直接プロパティを読み書きできません`$user->name`は機能しません)
* **メソッド経由のみで操作** - すべてのやりとりはGoで定義したメソッドを通じて行う必要があります
* **より良いカプセル化** - 内部データ構造は完全にGoコードによって制御されます
* **型安全性** - PHP側から誤った型で内部状態が破壊されるリスクがありません
* **よりクリーンなAPI** - 適切な公開インターフェースを設計することを強制します
このアプローチは優れたカプセル化を実現し、PHPコードがGoオブジェクトの内部状態を意図せずに破壊してしまうことを防ぎます。オブジェクトとのすべてのやりとりは、明示的に定義したメソッドを通じて行う必要があります。
#### クラスにメソッドを追加する
プロパティは直接アクセスできないため、不透明クラスとやりとりするには **メソッドを定義する必要があります**`//export_php:method`ディレクティブを使用して動作を定義します:
```go
//export_php:class User
type UserStruct struct {
Name string
Age int
}
//export_php:method User::getName(): string
func (us *UserStruct) GetUserName() unsafe.Pointer {
return frankenphp.PHPString(us.Name, false)
}
//export_php:method User::setAge(int $age): void
func (us *UserStruct) SetUserAge(age int64) {
us.Age = int(age)
}
//export_php:method User::getAge(): int
func (us *UserStruct) GetUserAge() int64 {
return int64(us.Age)
}
//export_php:method User::setNamePrefix(string $prefix = "User"): void
func (us *UserStruct) SetNamePrefix(prefix *C.zend_string) {
us.Name = frankenphp.GoString(unsafe.Pointer(prefix)) + ": " + us.Name
}
```
#### Nullableパラメータ
ジェネレーターは、PHPシグネチャにおける`?`プレフィックスを使用ったnullableパラメータをサポートしています。パラメータがnullableの場合、Go関数内ではポインタとして扱われ、PHP側で値が`null`だったかどうかを確認できます:
```go
//export_php:method User::updateInfo(?string $name, ?int $age, ?bool $active): void
func (us *UserStruct) UpdateInfo(name *C.zend_string, age *int64, active *bool) {
// nameが渡されたnullではないかチェック
if name != nil {
us.Name = frankenphp.GoString(unsafe.Pointer(name))
}
// ageが渡されたnullではないかチェック
if age != nil {
us.Age = int(*age)
}
// activeが渡されたnullではないかチェック
if active != nil {
us.Active = *active
}
}
```
**Nullableパラメータの重要なポイント**
* **プリミティブ型のnullable** (`?int`, `?float`, `?bool`) はGoではそれぞれポインタ (`*int64`, `*float64`, `*bool`) になります
* **nullable文字列** (`?string`) は `*C.zend_string` のままですが、`nil` になることがあります
* ポインタ値を逆参照する前に **`nil`をチェック** してください
* **PHPの`null`はGoの`nil`になります** - PHPが`null`を渡すと、Go関数は`nil`ポインタを受け取ります
> [!WARNING]
> 現在、クラスメソッドには次の制限があります。**配列とオブジェクトはパラメータ型や戻り値の型としてサポートされていません**。サポートされるのは`string`、`int`、`float`、`bool`、`void`(戻り値の型)といったスカラー型のみです。**nullableなスカラー型はすべてサポートされています** `?string`、`?int`、`?float`、`?bool`)。
拡張を生成した後、PHP側でクラスとそのメソッドを使用できるようになります。ただし**プロパティに直接アクセスできない**ことに注意してください:
```php
<?php
$user = new User();
// ✅ これは動作します - メソッドの使用
$user->setAge(25);
echo $user->getName(); // 出力: (empty、デフォルト値)
echo $user->getAge(); // 出力: 25
$user->setNamePrefix("Employee");
// ✅ これも動作します - nullableパラメータ
$user->updateInfo("John", 30, true); // すべて指定
$user->updateInfo("Jane", null, false); // Ageがnull
$user->updateInfo(null, 25, null); // Nameとactiveがnull
// ❌ これは動作しません - プロパティへの直接アクセス
// echo $user->name; // エラー: privateプロパティにアクセスできません
// $user->age = 30; // エラー: privateプロパティにアクセスできません
```
この設計により、Goコードがオブジェクトの状態へのアクセスと変更方法を完全に制御でき、より良いカプセル化と型安全性を提供します。
### 定数の宣言
ジェネレーターは、2つのディレクティブを使用してGo定数をPHPにエクスポートすることをサポートしていますグローバル定数用の`//export_php:const`とクラス定数用の`//export_php:classconstant`です。これにより、GoとPHPコード間で設定値、ステータスコード、その他の定数を共有できます。
#### グローバル定数
`//export_php:const`ディレクティブを使用してグローバルなPHP定数を作成できます
```go
//export_php:const
const MAX_CONNECTIONS = 100
//export_php:const
const API_VERSION = "1.2.3"
//export_php:const
const STATUS_OK = iota
//export_php:const
const STATUS_ERROR = iota
```
#### クラス定数
`//export_php:classconstant ClassName`ディレクティブを使用して、特定のPHPクラスに属する定数を作成できます
```go
//export_php:classconstant User
const STATUS_ACTIVE = 1
//export_php:classconstant User
const STATUS_INACTIVE = 0
//export_php:classconstant User
const ROLE_ADMIN = "admin"
//export_php:classconstant Order
const STATE_PENDING = iota
//export_php:classconstant Order
const STATE_PROCESSING = iota
//export_php:classconstant Order
const STATE_COMPLETED = iota
```
クラス定数は、PHPでクラス名スコープを使用してアクセスできます
```php
<?php
// グローバル定数
echo MAX_CONNECTIONS; // 100
echo API_VERSION; // "1.2.3"
// クラス定数
echo User::STATUS_ACTIVE; // 1
echo User::ROLE_ADMIN; // "admin"
echo Order::STATE_PENDING; // 0
```
ディレクティブは、文字列、整数、ブール値、浮動小数点数、iota定数など、さまざまな値の型をサポートしています。`iota`を使用する場合、ジェネレーターは自動的に連続した値0, 1, 2などを割り当てます。グローバル定数はPHPコードでグローバル定数として利用可能になり、クラス定数はpublicとしてそれぞれのクラスにスコープされます。整数を使用する場合、異なる記法バイナリ、16進数、8進数がサポートされ、PHPのスタブファイルにそのまま出力されます。
Go側のコードでは、いつも通り定数を使用できます。例えば、先ほど作成した`repeat_this()`関数を取り上げ、最後の引数を整数に変更してみましょう:
```go
import (
"C"
"github.com/dunglas/frankenphp"
"strings"
)
//export_php:const
const STR_REVERSE = iota
//export_php:const
const STR_NORMAL = iota
//export_php:classconstant StringProcessor
const MODE_LOWERCASE = 1
//export_php:classconstant StringProcessor
const MODE_UPPERCASE = 2
//export_php:function repeat_this(string $str, int $count, int $mode): string
func repeat_this(s *C.zend_string, count int64, mode int) unsafe.Pointer {
str := frankenphp.GoString(unsafe.Pointer(s))
result := strings.Repeat(str, int(count))
if mode == STR_REVERSE {
// 文字列を逆転
}
if mode == STR_NORMAL {
// 何もしない、定数を示すためのみ記載
}
return frankenphp.PHPString(result, false)
}
//export_php:class StringProcessor
type StringProcessorStruct struct {
// 内部フィールド
}
//export_php:method StringProcessor::process(string $input, int $mode): string
func (sp *StringProcessorStruct) Process(input *C.zend_string, mode int64) unsafe.Pointer {
str := frankenphp.GoString(unsafe.Pointer(input))
switch mode {
case MODE_LOWERCASE:
str = strings.ToLower(str)
case MODE_UPPERCASE:
str = strings.ToUpper(str)
}
return frankenphp.PHPString(str, false)
}
```
### 拡張モジュールの生成
ここでいよいよ、拡張モジュールを生成できるようになります。以下のコマンドでジェネレーターを実行できます:
```console
GEN_STUB_FILE=php-src/build/gen_stub.php frankenphp extension-init my_extension.go
```
> [!NOTE]
> `GEN_STUB_FILE`環境変数に、先ほどダウンロードしたPHPソースの`gen_stub.php`ファイルのパスを設定するのを忘れないでください。これは手動実装セクションで言及されているのと同じ`gen_stub.php`スクリプトです。
すべてがうまくいけば、`build`という名前の新しいディレクトリが作成されているはずです。このディレクトリには、生成されたPHP関数スタブを含む`my_extension.go`ファイルなど、拡張用の生成されたファイルが含まれています。
### 生成された拡張モジュールをFrankenPHPへ統合する
拡張モジュールがコンパイルされ、FrankenPHPに統合される準備が整いました。これを行うには、FrankenPHPのコンパイル方法を学ぶために、FrankenPHPの[コンパイルドキュメント](compile.md)を参照してください。`--with`フラグを使用してモジュールを追加し、モジュールのパスを指定します:
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with github.com/my-account/my-module/build
```
このとき、生成ステップで作成された`/build`サブディレクトリを指していることに注意してください。ただし、これは必須ではなく、生成されたファイルをモジュールのディレクトリにコピーして、直接それを指定することも可能です。
### 生成された拡張モジュールのテスト
作成した関数とクラスをテストするPHPファイルを作成しましょう。例えば、以下の内容で`index.php`ファイルを作成します:
```php
<?php
// グローバル定数を使用
var_dump(repeat_this('Hello World', 5, STR_REVERSE));
// クラス定数を使用
$processor = new StringProcessor();
echo $processor->process('Hello World', StringProcessor::MODE_LOWERCASE); // "hello world"
echo $processor->process('Hello World', StringProcessor::MODE_UPPERCASE); // "HELLO WORLD"
```
前のセクションで示したように拡張モジュールをFrankenPHPに統合し、`./frankenphp php-server`を使用してこのテストファイルを実行することで、拡張モジュールが動作しているのを確認できるはずです。
## 手動実装
拡張モジュールの仕組みを理解したい、または拡張モジュールを完全に制御したい場合は、手動で書くこともできます。このアプローチは完全な制御を実現できますが、より多くのボイラープレートコードが必要になります。
### 基本的な関数
ここでは、新しいネイティブ関数を定義するシンプルなPHP拡張モジュールをGoで手動実装する方法を紹介します。この関数はPHPから呼び出され、その関数がgoroutineを使ってCaddyのログにメッセージ出力するという処理を行います。この関数は引数を取らず、戻り値もありません。
#### Go関数の定義
モジュール内で、PHPから呼び出される新しいネイティブ関数を定義する必要があります。これを行うには、例えば`extension.go`のように任意の名前でファイルを作成し、以下のコードを追加します:
```go
package ext_go
//#include "extension.h"
import "C"
import (
"unsafe"
"github.com/caddyserver/caddy/v2"
"github.com/dunglas/frankenphp"
)
func init() {
frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry))
}
//export go_print_something
func go_print_something() {
go func() {
caddy.Log().Info("Hello from a goroutine!")
}()
}
```
`frankenphp.RegisterExtension()`関数は、内部のPHP登録ロジックを処理することで拡張登録プロセスを簡素化します。`go_print_something`関数は`//export`ディレクティブを使用して、CGOのおかげで、これから書くCコードでアクセスできるようになることを示しています。
この例では、新しい関数がCaddyのログにメッセージ出力するgoroutineをトリガーします。
#### PHP関数の定義
PHPがGo関数を呼び出せるようにするには、対応するPHP関数を定義する必要があります。このために、例えば`extension.stub.php`のようにスタブファイルを作成し、以下のコードを記述します:
```php
<?php
/** @generate-class-entries */
function go_print(): void {}
```
このファイルはPHPから呼び出される`go_print()`関数のシグネチャを定義します。`@generate-class-entries`ディレクティブは、PHPがこの拡張モジュールのために関数エントリを自動生成することを可能にします。
これは手動ではなく、PHPソースで提供されるスクリプトを使用して行いますPHPソースが置かれている場所に基づいて`gen_stub.php`スクリプトのパスを調整してください):
```bash
php ../php-src/build/gen_stub.php extension.stub.php
```
このスクリプトは、PHPがこの関数の定義および呼び出し方法を知るのに必要な情報を含む`extension_arginfo.h`という名前のファイルを生成します。
#### GoとC間のブリッジの作成
今度は、GoとC間をつなぐブリッジを書く必要があります。モジュールディレクトリに`extension.h`という名前のファイルを作成し、以下の内容を書きます:
```c
#ifndef _EXTENSION_H
#define _EXTENSION_H
#include <php.h>
extern zend_module_entry ext_module_entry;
#endif
```
次に、以下のステップを実行する`extension.c`という名前のファイルを作成します:
* PHPヘッダーをインクルードする
* 新しいネイティブPHP関数`go_print()`を宣言する
* 拡張モジュールのメタデータを宣言する
まずは必要なヘッダーのインクルードから始めましょう:
```c
#include <php.h>
#include "extension.h"
#include "extension_arginfo.h"
// Goによってエクスポートされたシンボルを含みます
#include "_cgo_export.h"
```
次に、PHP関数をネイティブ言語関数として定義します
```c
PHP_FUNCTION(go_print)
{
ZEND_PARSE_PARAMETERS_NONE();
go_print_something();
}
zend_module_entry ext_module_entry = {
STANDARD_MODULE_HEADER,
"ext_go",
ext_functions, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
"0.1.1",
STANDARD_MODULE_PROPERTIES
};
```
この場合、関数はパラメータを取らず、何も返しません。単に`//export`ディレクティブを使用してエクスポートした、先ほど定義したGo関数を呼び出します。
最後に、名前、バージョン、プロパティなど、拡張のメタデータを`zend_module_entry`構造体で定義します。この情報はPHPが私たちの拡張モジュールを認識してロードするために必要です。`ext_functions`は定義したPHP関数へのポインタの配列であり、`gen_stub.php`スクリプトによって自動生成された`extension_arginfo.h`ファイル内に定義されています。
拡張モジュールの登録は、Goコード内で呼び出しているFrankenPHPの`RegisterExtension()`関数によって自動的に処理されます。
### 高度な使用方法
基本的なPHP拡張をGoで作成する方法が分かったところで、少し例を複雑にしてみましょう。今度は文字列を引数として受け取り、その大文字版を返すPHP関数を作成します。
#### PHP関数スタブの定義
新しいPHP関数を定義するために、`extension.stub.php`ファイルを修正し、次の関数シグネチャを含めます:
```php
<?php
/** @generate-class-entries */
/**
* Converts a string to uppercase.
*
* @param string $string The string to convert.
* @return string The uppercase version of the string.
*/
function go_upper(string $string): string {}
```
> [!TIP]
> 関数のドキュメントを軽視しないでください!拡張スタブを他の開発者と共有する際、拡張機能の使い方や提供している機能を伝えるための重要な手段になります。
`gen_stub.php`スクリプトでスタブファイルを再生成すると、`extension_arginfo.h`ファイルは以下のようになるはずです:
```c
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_go_upper, 0, 1, IS_STRING, 0)
ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0)
ZEND_END_ARG_INFO()
ZEND_FUNCTION(go_upper);
static const zend_function_entry ext_functions[] = {
ZEND_FE(go_upper, arginfo_go_upper)
ZEND_FE_END
};
```
この出力から、`go_upper`関数が`string`型の引数を1つ受け取り、`string`型の戻り値を返すことが定義されていのがわかります。
#### GoとPHP/C間の型変換Type Juggling
Go関数はPHPの文字列を引数として直接受け取ることはできません。そのためPHPの文字列をGoの文字列へ変換する必要があります。幸いなことに、FrankenPHPは、ジェネレーターアプローチで見たものと同様に、PHP文字列とGo文字列間の変換を処理するヘルパー関数を提供しています。
ヘッダーファイルはシンプルなままです:
```c
#ifndef _EXTENSION_H
#define _EXTENSION_H
#include <php.h>
extern zend_module_entry ext_module_entry;
#endif
```
次に、`extension.c`ファイルにGoとC間のブリッジを書きます。ここではPHPの文字列を直接Go関数に渡します
```c
PHP_FUNCTION(go_upper)
{
zend_string *str;
ZEND_PARSE_PARAMETERS_START(1, 1)
Z_PARAM_STR(str)
ZEND_PARSE_PARAMETERS_END();
zend_string *result = go_upper(str);
RETVAL_STR(result);
}
```
`ZEND_PARSE_PARAMETERS_START`や引数のパースについては、[PHP Internals Book](https://www.phpinternalsbook.com/php7/extensions_design/php_functions.html#parsing-parameters-zend-parse-parameters)の該当ページで詳しく学ぶことができます。この例では、関数が`zend_string`として`string`型の必須引数を1つ取ることをPHPに伝えています。その後、この文字列を直接Go関数に渡し、`RETVAL_STR`を使用して結果を返します。
残るはただ一つ、Go側で`go_upper`関数を実装するだけです。
#### Go関数の実装
Go側の関数では`*C.zend_string`を引数として受け取り、FrankenPHPのヘルパー関数を使用してGoの文字列に変換し、処理を行ったうえで、結果を新たな`*C.zend_string`として返します。メモリ管理と変換の複雑さは、ヘルパー関数がすべて対応してくれます。
```go
import "strings"
//export go_upper
func go_upper(s *C.zend_string) *C.zend_string {
str := frankenphp.GoString(unsafe.Pointer(s))
upper := strings.ToUpper(str)
return (*C.zend_string)(frankenphp.PHPString(upper, false))
}
```
このアプローチは、手動メモリ管理よりもはるかにクリーンで安全です。FrankenPHPのヘルパー関数は、PHPの`zend_string`形式とGoの文字列間の変換を自動的に処理してくれます。`PHPString()``false`引数を指定していることで、新しい非永続文字列(リクエストの終了時に解放される)を作成したいことを示しています。
> [!TIP]
> この例ではエラーハンドリングを省略していますが、Go関数内でポインタが`nil`ではないこと、渡されたデータが有効であることを常に確認するべきです。
### 拡張モジュールのFrankenPHPへの統合
拡張モジュールがコンパイルされ、FrankenPHPに統合される準備が整いました。手順についてはFrankenPHPのコンパイル方法を学ぶために、FrankenPHPの[コンパイルドキュメント](compile.md)を参照してください。`--with`フラグを使用してモジュールを追加し、モジュールのパスを指定します:
```console
CGO_ENABLED=1 \
XCADDY_GO_BUILD_FLAGS="-ldflags='-w -s' -tags=nobadger,nomysql,nopgx" \
CGO_CFLAGS=$(php-config --includes) \
CGO_LDFLAGS="$(php-config --ldflags) $(php-config --libs)" \
xcaddy build \
--output frankenphp \
--with github.com/my-account/my-module
```
これで完了です拡張モジュールがFrankenPHPに統合され、PHPコードで利用できるようになりました。
### 拡張モジュールのテスト
拡張モジュールをFrankenPHPに統合したら、実装した関数を試すための`index.php`ファイルを作成します:
```php
<?php
// 基本関数のテスト
go_print();
// 高度な関数のテスト
echo go_upper("hello world") . "\n";
```
このファイルを使用して`./frankenphp php-server`でFrankenPHPを実行でき、拡張モジュールが動作しているのを確認できるはずです。

31
docs/ja/github-actions.md Normal file
View File

@@ -0,0 +1,31 @@
# GitHub Actionsの使用
このリポジトリでは、承認されたプルリクエストごと、またはセットアップ後のあなた自身のフォークで、
Dockerイメージをビルドして[Docker Hub](https://hub.docker.com/r/dunglas/frankenphp)にデプロイします。
## GitHub Actionsのセットアップ
リポジトリ設定のシークレットで、以下のシークレットを追加してください:
- `REGISTRY_LOGIN_SERVER`: 使用するDockerレジストリ`docker.io`
- `REGISTRY_USERNAME`: レジストリログイン用のユーザー名(例:`dunglas`
- `REGISTRY_PASSWORD`: レジストリログイン用のパスワード(例:アクセスキー)
- `IMAGE_NAME`: イメージの名前(例:`dunglas/frankenphp`
## イメージのビルドとプッシュ
1. プルリクエストを作成するか、フォークにプッシュします
2. GitHub Actionsがイメージをビルドし、テストを実行します
3. ビルドが成功した場合、イメージは`pr-x``x`はPR番号をタグとしてレジストリにプッシュされます
## イメージのデプロイ
1. プルリクエストがマージされると、GitHub Actionsが再度テストを実行し、新しいイメージをビルドします
2. ビルドが成功した場合、Dockerレジストリの`main`タグが更新されます
## リリース
1. リポジトリで新しいタグを作成します
2. GitHub Actionsがイメージをビルドし、テストを実行します
3. ビルドが成功した場合、イメージはタグ名をタグとしてレジストリにプッシュされます(例:`v1.2.3``v1.2`が作成されます)
4. `latest`タグも更新されます

143
docs/ja/known-issues.md Normal file
View File

@@ -0,0 +1,143 @@
# 既知の問題
## 未対応のPHP拡張モジュール
以下の拡張モジュールはFrankenPHPと互換性がないことが確認されています
| 名前 | 理由 | 代替手段 |
| ----------------------------------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------- |
| [imap](https://www.php.net/manual/en/imap.installation.php) | スレッドセーフでない | [javanile/php-imap2](https://github.com/javanile/php-imap2), [webklex/php-imap](https://github.com/Webklex/php-imap) |
| [newrelic](https://docs.newrelic.com/docs/apm/agents/php-agent/getting-started/introduction-new-relic-php/) | スレッドセーフでない | - |
## バグのあるPHP拡張モジュール
以下の拡張モジュールはFrankenPHPとの組み合わせで既知のバグや予期しない動作が確認されています
| 名前 | 問題 |
| ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [ext-openssl](https://www.php.net/manual/en/book.openssl.php) | FrankenPHPの静的ビルドmusl libcでビルドを使用した場合、高負荷時にOpenSSL拡張がクラッシュすることがあります。回避策として動的リンクのビルドDockerイメージで使用されているものを使用してください。このバグは[PHP側で追跡中](https://github.com/php/php-src/issues/13648)です。 |
## get_browser
[get_browser()](https://www.php.net/manual/en/function.get-browser.php)関数は継続使用するとパフォーマンスが悪化することが確認されています。回避策として、User Agentごとの結果をキャッシュ[APCu](https://www.php.net/manual/en/book.apcu.php)を利用してください。User Agentごとの結果は静的なためです。
## スタンドアロンバイナリおよびAlpineベースのDockerイメージ
スタンドアロンバイナリおよびAlpineベースのDockerイメージ`dunglas/frankenphp:*-alpine`)は、バイナリサイズを小さく保つために[glibc and friends](https://www.etalabs.net/compare_libcs.html)ではなく[musl libc](https://musl.libc.org/)を使用しています。これによりいくつかの互換性問題が発生する可能性があります。特に、globフラグ`GLOB_BRACE`は [サポートされていません](https://www.php.net/manual/en/function.glob.php) 。
## Dockerで`https://127.0.0.1`を使用する
デフォルトでは、FrankenPHPは`localhost`用のTLS証明書を生成します。
これはローカル開発における最も簡単かつ推奨される方法です。
どうしても`127.0.0.1`をホストとして使用したい場合は、サーバー名を`127.0.0.1`に設定してその証明書を生成させることが可能です。
ただし、[Dockerのネットワークシステム](https://docs.docker.com/network/)の仕組みにより、Dockerを使用する場合はこれだけでは不十分です。
この場合、`curl: (35) LibreSSL/3.3.6: error:1404B438:SSL routines:ST_CONNECT:tlsv1 alert internal error`のようなTLSエラーが発生します。
Linuxを使用している場合、[ホストネットワークドライバー](https://docs.docker.com/network/network-tutorial-host/)を使用することで、この問題を解決できます:
```console
docker run \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
--network host \
dunglas/frankenphp
```
ホストネットワークドライバーはMacとWindowsではサポートされていません。これらのプラットフォームでは、コンテナのIPアドレスを推測してサーバー名に含める必要があります。
`docker network inspect bridge`を実行し、`Containers`キーを確認して`IPv4Address`にある現在割り当てられている最後のIPアドレスを特定し、それに1を加えます。コンテナがまだ実行されていない場合、最初に割り当てられるIPアドレスは通常`172.17.0.2`です。
そして、これを`SERVER_NAME`環境変数に含めます:
```console
docker run \
-e SERVER_NAME="127.0.0.1, 172.17.0.3" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
> [!CAUTION]
>
> `172.17.0.3`の部分は、実際にコンテナに割り当てられるIPに置き換えてください。
これでホストマシンから`https://127.0.0.1`へアクセスできるはずです。
うまくいかない場合は、FrankenPHPをデバッグモードで起動して問題を特定してみてください
```console
docker run \
-e CADDY_GLOBAL_OPTIONS="debug" \
-e SERVER_NAME="127.0.0.1" \
-v $PWD:/app/public \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## `@php` を参照するComposerスクリプト
[Composerスクリプト](https://getcomposer.org/doc/articles/scripts.md)では、いくつかのタスクでPHPバイナリを実行したい場合があります。例えば、[Laravelプロジェクト](laravel.md)で`@php artisan package:discover --ansi`を実行する場合です。しかし現在これは以下の2つの理由で[失敗します](https://github.com/dunglas/frankenphp/issues/483#issuecomment-1899890915)
- ComposerはFrankenPHPバイナリを呼び出す方法を知りません
- Composerはコマンドで`-d`フラグを使用してPHP設定を追加する場合があり、FrankenPHPはまだサポートしていません
回避策として、未サポートのパラメータを削除してFrankenPHPを呼び出すシェルスクリプトを`/usr/local/bin/php`に作成できます:
```bash
#!/usr/bin/env bash
args=("$@")
index=0
for i in "$@"
do
if [ "$i" == "-d" ]; then
unset 'args[$index]'
unset 'args[$index+1]'
fi
index=$((index+1))
done
/usr/local/bin/frankenphp php-cli ${args[@]}
```
次に、環境変数`PHP_BINARY`にこの`php`スクリプトのパスを設定してComposerを実行します
```console
export PHP_BINARY=/usr/local/bin/php
composer install
```
## 静的バイナリでのTLS/SSL問題のトラブルシューティング
静的バイナリを使用する場合、例えばSTARTTLSを使用してメールを送信する際に以下のTLS関連エラーが発生する可能性があります
```text
Unable to connect with STARTTLS: stream_socket_enable_crypto(): SSL operation failed with code 5. OpenSSL Error messages:
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:80000002:system library::No such file or directory
error:0A000086:SSL routines::certificate verify failed
```
静的バイナリにはTLS証明書がバンドルされていないため、OpenSSLにローカルのCA証明書の位置を明示する必要があります。
[`openssl_get_cert_locations()`](https://www.php.net/manual/en/function.openssl-get-cert-locations.php)の出力を調べて、
CA証明書をどこにインストールすべきか確認し、その場所に保存してください。
> [!WARNING]
>
> WebとCLIコンテキストでは設定が異なる場合があります。
> 適切なコンテキストで`openssl_get_cert_locations()`を実行してください。
[Mozillaから抽出されたCA証明書はcurlのサイトでダウンロードできます](https://curl.se/docs/caextract.html)。
または、Debian、Ubuntu、Alpineなどのディストリビューションでも、これらの証明書を含む`ca-certificates`というパッケージを提供しています。
`SSL_CERT_FILE`および`SSL_CERT_DIR`を使用してOpenSSLにCA証明書を探す場所をヒントとして与えることも可能です
```console
# TLS 証明書の環境変数を設定
export SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
export SSL_CERT_DIR=/etc/ssl/certs
```

183
docs/ja/laravel.md Normal file
View File

@@ -0,0 +1,183 @@
# Laravel
## Docker
FrankenPHPを使用して[Laravel](https://laravel.com)のWebアプリケーションを配信するのは簡単で、公式Dockerイメージの`/app`ディレクトリにプロジェクトをマウントするだけです。
Laravelアプリのメインディレクトリからこのコマンドを実行してください
```console
docker run -p 80:80 -p 443:443 -p 443:443/udp -v $PWD:/app dunglas/frankenphp
```
お楽しみください!
## ローカルインストール
または、ローカルマシンでFrankenPHPを使用してLaravelプロジェクトを実行することもできます
1. [使用しているシステムに対応するバイナリをダウンロードします](../#standalone-binary)
2. Laravelプロジェクトのルートディレクトリに`Caddyfile`という名前のファイルを作成し、以下の設定を追加します:
```caddyfile
{
frankenphp
}
# サーバーのドメイン名
localhost {
# webroot を public/ ディレクトリに設定
root public/
# 圧縮を有効にする(任意)
encode zstd br gzip
# public/ ディレクトリ内の PHP ファイルを実行し、アセットを提供
php_server {
try_files {path} index.php
}
}
```
3. LaravelプロジェクトのルートディレクトリからFrankenPHPを起動します `frankenphp run`
## Laravel Octane
OctaneはComposerパッケージマネージャーを使用してインストールできます
```console
composer require laravel/octane
```
Octaneをインストールした後、`octane:install` Artisanコマンドを実行すると、Octaneの設定ファイルがアプリケーションにインストールされます
```console
php artisan octane:install --server=frankenphp
```
Octaneサーバーは`octane:frankenphp` Artisanコマンドで開始できます。
```console
php artisan octane:frankenphp
```
`octane:frankenphp`コマンドは以下のオプションが利用可能です:
- `--host`: サーバーがバインドするIPアドレスデフォルト`127.0.0.1`
- `--port`: サーバーが使用するポート(デフォルト: `8000`
- `--admin-port`: 管理サーバーが使用するポート(デフォルト: `2019`
- `--workers`: リクエスト処理に使うワーカー数(デフォルト: `auto`
- `--max-requests`: サーバーを再起動するまでに処理するリクエスト数(デフォルト: `500`
- `--caddyfile`: FrankenPHPの`Caddyfile`ファイルのパス(デフォルト: [Laravel OctaneのスタブCaddyfile](https://github.com/laravel/octane/blob/2.x/src/Commands/stubs/Caddyfile)
- `--https`: HTTPS、HTTP/2、HTTP/3を有効にし、証明書を自動的に生成・更新する
- `--http-redirect`: HTTPからHTTPSへのリダイレクトを有効にする--httpsオプション指定時のみ有効
- `--watch`: アプリケーションが変更されたときに自動的にサーバーをリロードする
- `--poll`: ネットワーク越しのファイル監視のためにファイルシステムポーリングを使用する
- `--log-level`: ネイティブCaddyロガーを使用して、指定されたログレベル以上でログメッセージを記録する
> [!TIP]
> 構造化されたJSONログログ分析ソリューションを使用する際に便利を取得するには、明示的に`--log-level`オプションを指定してください。
詳しくは[Laravel Octaneの公式ドキュメント](https://laravel.com/docs/octane)をご覧ください。
## Laravelアプリのスタンドアロンバイナリ化
[FrankenPHPのアプリケーション埋め込み機能](embed.md)を使用して、Laravelアプリをスタンドアロンバイナリとして
配布することが可能です。
LaravelアプリをLinux用のスタンドアロンバイナリとしてパッケージ化するには、以下の手順に従ってください
1. アプリのリポジトリに`static-build.Dockerfile`という名前のファイルを作成します:
```dockerfile
FROM --platform=linux/amd64 dunglas/frankenphp:static-builder
# アプリをコピー
WORKDIR /go/src/app/dist/app
COPY . .
# スペースを節約するためにテストやその他の不要なファイルを削除
# 代わりに .dockerignore に記述して除外することも可能
RUN rm -Rf tests/
# .envファイルをコピー
RUN cp .env.example .env
# APP_ENV と APP_DEBUG を本番用に変更
RUN sed -i'' -e 's/^APP_ENV=.*/APP_ENV=production/' -e 's/^APP_DEBUG=.*/APP_DEBUG=false/' .env
# 必要に応じて .env ファイルにさらに変更を加える
# 依存関係をインストール
RUN composer install --ignore-platform-reqs --no-dev -a
# 静的バイナリをビルド
WORKDIR /go/src/app/
RUN EMBED=dist/app/ ./build-static.sh
```
> [!CAUTION]
>
> 一部の`.dockerignore`ファイルは
> `vendor/`ディレクトリや`.env`ファイルを無視します。ビルド前に`.dockerignore`ファイルを調整または削除してください。
2. ビルドします:
```console
docker build -t static-laravel-app -f static-build.Dockerfile .
```
3. バイナリを取り出します:
```console
docker cp $(docker create --name static-laravel-app-tmp static-laravel-app):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp ; docker rm static-laravel-app-tmp
```
4. キャッシュを構築します:
```console
frankenphp php-cli artisan optimize
```
5. データベースマイグレーションを実行します(ある場合):
```console
frankenphp php-cli artisan migrate
```
6. アプリの秘密鍵を生成します:
```console
frankenphp php-cli artisan key:generate
```
7. サーバーを起動します:
```console
frankenphp php-server
```
これで、アプリの準備は完了です!
利用可能なオプションや他のOSでバイナリをビルドする方法については、[アプリケーション埋め込み](embed.md)ドキュメントをご覧ください。
### ストレージパスの変更
Laravelはアップロードされたファイルやキャッシュ、ログなどをデフォルトでアプリケーションの`storage/`ディレクトリに保存します。
しかし、これは埋め込みアプリケーションには適していません。なぜなら、アプリの新しいバージョンごとに異なる一時ディレクトリに展開されるためです。
この問題を回避するには、`LARAVEL_STORAGE_PATH`環境変数を設定(例:`.env`ファイル内)するか、 `Illuminate\Foundation\Application::useStoragePath()`メソッドを呼び出して、一時ディレクトリの外にある任意のディレクトリを使用してください。
### スタンドアロンバイナリでOctaneを実行する
Laravel Octaneアプリもスタンドアロンバイナリとしてパッケージ化することが可能です
そのためには、[Octaneを正しくインストール](#laravel-octane)し、[前のセクション](#laravelアプリのスタンドアロンバイナリ化)で説明した手順に従ってください。
次に、Octaneを通じてワーカーモードでFrankenPHPを起動するには、以下を実行してください
```console
PATH="$PWD:$PATH" frankenphp php-cli artisan octane:frankenphp
```
> [!CAUTION]
>
> コマンドを動作させるためには、スタンドアロンバイナリのファイル名が**必ず**`frankenphp`でなければなりません。
> Octaneは`frankenphp`という名前の実行ファイルがパス上に存在することを前提としています。

15
docs/ja/mercure.md Normal file
View File

@@ -0,0 +1,15 @@
# リアルタイム
FrankenPHPには組み込みの[Mercure](https://mercure.rocks)ハブが付属しています!
Mercureを使用すると、接続されているすべてのデバイスにリアルタイムイベントをプッシュでき、各デバイスは即座にJavaScriptイベントを受信します。
JSライブラリやSDKは必要ありません
![Mercure](mercure-hub.png)
Mercureハブを有効にするには、[Mercureのサイト](https://mercure.rocks/docs/hub/config)で説明されているように`Caddyfile`を更新してください。
Mercureハブのパスは`/.well-known/mercure`です。
FrankenPHPをDocker内で実行している場合、完全な送信URLは`http://php/.well-known/mercure`のようになります。ここでの`php`はFrankenPHPを実行するコンテナの名前です。
コードからMercureの更新をプッシュするには、[Symfony Mercure Component](https://symfony.com/components/Mercure)をお勧めします。なお、Symfonyのフルスタックフレームワークは必要ありません。

17
docs/ja/metrics.md Normal file
View File

@@ -0,0 +1,17 @@
# メトリクス
[Caddyのメトリクス](https://caddyserver.com/docs/metrics)が有効になっていると、FrankenPHPは以下のメトリクスを公開します
- `frankenphp_total_threads`: PHPスレッドの総数
- `frankenphp_busy_threads`: 現在リクエストを処理中のPHPスレッド数。なお、実行中のワーカーは常にスレッドを消費します
- `frankenphp_queue_depth`: 通常のキューに入っているリクエストの数
- `frankenphp_total_workers{worker="[worker_name]"}`: ワーカーの総数
- `frankenphp_busy_workers{worker="[worker_name]"}`: 現在リクエストを処理中のワーカーの数
- `frankenphp_worker_request_time{worker="[worker_name]"}`: すべてのワーカーがリクエスト処理に費やした時間
- `frankenphp_worker_request_count{worker="[worker_name]"}`: すべてのワーカーが処理したリクエスト数
- `frankenphp_ready_workers{worker="[worker_name]"}`: 少なくとも一度は `frankenphp_handle_request` を呼び出したワーカーの数
- `frankenphp_worker_crashes{worker="[worker_name]"}`: ワーカーが予期せず終了した回数
- `frankenphp_worker_restarts{worker="[worker_name]"}`: ワーカーが意図的に再起動された回数
- `frankenphp_worker_queue_depth{worker="[worker_name]"}`: キューに入っているリクエストの数
ワーカーメトリクスの`[worker_name]`プレースホルダーは、Caddyfileに指定されたワーカー名に置き換えられます。ワーカー名が指定されていない場合は、ワーカーファイルの絶対パスが使用されます。

157
docs/ja/performance.md Normal file
View File

@@ -0,0 +1,157 @@
# パフォーマンス
デフォルトでは、FrankenPHPはパフォーマンスと使いやすさのバランスが取れた構成を提供するよう設計されています。
ただし、適切な設定により、パフォーマンスを大幅に向上させることが可能です。
## スレッド数とワーカー数
デフォルトでは、FrankenPHPは利用可能なCPU数の2倍のスレッドとワーカーワーカーモードでを開始します。
適切な値は、アプリケーションの書き方、機能、ハードウェアに大きく依存します。
これらの値を調整することを強く推奨します。最適なシステムの安定性のためには、`num_threads` x `memory_limit` < `available_memory`とすることをお勧めします。
適切な値を見つけるには、実際のトラフィックをシミュレートした負荷テストを実行するのが最も効果的です。
そのためのツールとして、[k6](https://k6.io)や[Gatling](https://gatling.io)が有用です。
スレッド数を設定するには、`php_server``php`ディレクティブ内の`num_threads`オプションを使用してください。
ワーカー数を変更するには、`frankenphp`ディレクティブ内の`worker`セクションにある`num`オプションを使用してください。
### `max_threads`
実際のトラフィックがどのようなものになるかを正確に把握できれば理想ですが、現実のアプリケーションでは
予測困難な挙動が多いものです。`max_threads`[設定](config.md#caddyfile-config) により、FrankenPHPは指定された制限まで実行時に追加スレッドを自動的に生成できます。
`max_threads`はトラフィックを処理するために必要なスレッド数を把握するのに役立ち、レイテンシのスパイクに対してサーバーをより回復力のあるものにできます。
`auto`に設定すると、制限は`php.ini``memory_limit`に基づいて推定されます。推定できない場合、
`auto`は代わりに`num_threads`の2倍がデフォルトになります。`auto`は必要なスレッド数を大幅に過小評価する可能性があることに留意してください。
`max_threads`はPHP FPMの[pm.max_children](https://www.php.net/manual/en/install.fpm.configuration.php#pm.max-children)に似ています。主な違いは、FrankenPHPがプロセスではなくスレッドを使用し、
必要に応じて異なるワーカースクリプトと「クラシックモード」間で自動的に委譲することです。
## ワーカーモード
[ワーカーモード](worker.md)を有効にするとパフォーマンスが劇的に向上しますが、
アプリがこのモードと互換性があるように適応する必要があります:
ワーカースクリプトを作成し、アプリがメモリリークしていないことを確認する必要があります。
## muslを使用しない
公式Dockerイメージと私たちが提供するデフォルトバイナリのAlpine Linuxバリアントは、[musl libc](https://musl.libc.org)を使用しています。
PHPは、従来のGNUライブラリの代わりにこの代替Cライブラリを使用すると[遅くなる](https://gitlab.alpinelinux.org/alpine/aports/-/issues/14381)ことが知られており、
特にFrankenPHPに必要なZTSモードスレッドセーフでコンパイルされた場合です。高度にスレッド化された環境では、差が大きくなる可能性があります。
また、[一部のバグはmuslを使用した場合にのみ発生します](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl)。
本番環境では、glibcにリンクされたFrankenPHPを使用することをお勧めします。
これは、Debian Dockerイメージデフォルトを使用するか、[リリースページ](https://github.com/php/frankenphp/releases)から -gnu サフィックス付きバイナリをダウンロードするか、あるいは[FrankenPHPをソースからコンパイル](compile.md)することで実現できます。
または、[mimalloc allocator](https://github.com/microsoft/mimalloc)でコンパイルされた静的muslバイナリも提供しており、これによりスレッド環境での問題を軽減できます。
## Go Runtime設定
FrankenPHPはGoで書かれています。
一般的に、Go runtimeは特別な設定を必要としませんが、特定の状況では、
特定の設定でパフォーマンスが向上する場合があります。
おそらく`GODEBUG`環境変数を`cgocheck=0`に設定したいでしょうFrankenPHP Dockerイメージのデフォルト
FrankenPHPをコンテナDocker、Kubernetes、LXC...)で実行しており、コンテナで利用可能なメモリを制限している場合は、
`GOMEMLIMIT`環境変数に利用可能なメモリ量を設定してください。
詳細については、Go ランタイムを最大限に活用するために、[この主題に特化したGoドキュメントページ](https://pkg.go.dev/runtime#hdr-Environment_Variables)を読むことを強く推奨します。
## `file_server`
デフォルトでは、`php_server`ディレクティブは自動的にファイルサーバーを設定して
ルートディレクトリに保存された静的ファイル(アセット)を配信します。
この機能は便利ですが、コストがかかります。
無効にするには、以下の設定を使用してください:
```caddyfile
php_server {
file_server off
}
```
## `try_files`
`php_server`は、静的ファイルとPHPファイルに加えて、アプリケーションのインデックスファイル
およびディレクトリインデックスファイル(`/path/` -> `/path/index.php`)も試行します。ディレクトリインデックスが不要な場合、
次のように`try_files`を明示的に定義して無効にできます:
```caddyfile
php_server {
try_files {path} index.php
root /root/to/your/app # ここで root を明示的に追加すると、キャッシュの効率が向上します
}
```
これにより、不要なファイルの操作の回数を大幅に削減できます。
ファイルシステムへの不要な操作を完全にゼロにする代替アプローチとして、`php`ディレクティブを使用し、
パスによってPHPファイルとそれ以外を分ける方法があります。アプリケーション全体が1つのエントリーファイルで提供される場合、この方法は有効です。
たとえば`/assets`フォルダの背後で静的ファイルを提供する[設定](config.md#caddyfile-config)は次のようになります:
```caddyfile
route {
@assets {
path /assets/*
}
# /assets 以下のリクエストはファイルサーバーが処理する
file_server @assets {
root /root/to/your/app
}
# /assets 以外のすべてのリクエストは index または worker の PHP ファイルで処理する
rewrite index.php
php {
root /root/to/your/app # ここで root を明示的に追加すると、キャッシュの効率が向上します
}
}
```
## プレースホルダー
`root`および`env`ディレクティブ内では、[プレースホルダー](https://caddyserver.com/docs/conventions#placeholders)を使用できます。
ただし、これによりこれらの値をキャッシュすることができなくなり、大幅なパフォーマンスコストが発生します。
可能であれば、これらのディレクティブではプレースホルダーの使用を避けてください。
## `resolve_root_symlink`
デフォルトでは、ドキュメントルートがシンボリックリンクである場合、FrankenPHP はそれを自動的に解決します(これは PHP が正しく動作するために必要です)。
ドキュメントルートがシンボリックリンクでない場合、この機能を無効にできます。
```caddyfile
php_server {
resolve_root_symlink false
}
```
この設定は、`root`ディレクティブに[プレースホルダー](https://caddyserver.com/docs/conventions#placeholders)が含まれている場合にパフォーマンスを向上させます。
それ以外の場合の効果はごくわずかです。
## ログ
ログ出力は当然ながら非常に有用ですが、その性質上、
I/O操作およびメモリ確保が必要となり、パフォーマンスを大幅に低下させます。
[ログレベルを正しく設定](https://caddyserver.com/docs/caddyfile/options#log)し、
必要なもののみをログに記録するようにしてください。
## PHPパフォーマンス
FrankenPHPは公式のPHPインタープリターを使用しています。
通常のPHPに関するパフォーマンス最適化はすべてFrankenPHPでも有効です。
特に以下の点を確認してください:
- [OPcache](https://www.php.net/manual/en/book.opcache.php)がインストールされ、有効化され、適切に設定されていること
- [Composer autoloader optimizations](https://getcomposer.org/doc/articles/autoloader-optimization.md)を有効にすること
- `realpath`キャッシュがアプリケーションのニーズに合わせて十分な大きさであること
- [preloading](https://www.php.net/manual/en/opcache.preloading.php)を使用すること
詳細については、[Symfonyの専用ドキュメントエントリ](https://symfony.com/doc/current/performance.html)をお読みください
Symfonyを使用していなくても、多くのヒントが役立ちます

144
docs/ja/production.md Normal file
View File

@@ -0,0 +1,144 @@
# 本番環境でのデプロイ
このチュートリアルでは、Docker Composeを使用して単一サーバーにPHPアプリケーションをデプロイする方法を学びます。
Symfonyを使用している場合は、Symfony Dockerプロジェクトの「[本番環境へのデプロイ](https://github.com/dunglas/symfony-docker/blob/main/docs/production.md)」ドキュメントを参照してください。
API Platformを使用している場合は、[フレームワークのデプロイドキュメント](https://api-platform.com/docs/deployment/)を参照してください。
## アプリの準備
まず、PHPプロジェクトのルートディレクトリに`Dockerfile`を作成します:
```dockerfile
FROM dunglas/frankenphp
# "your-domain-name.example.com" を実際のドメイン名に置き換えてください
ENV SERVER_NAME=your-domain-name.example.com
# HTTPSを無効にしたい場合は、次の値を代わりに使用してください
#ENV SERVER_NAME=:80
# プロジェクトで "public" ディレクトリをWebルートとして使用していない場合、ここで設定できます:
# ENV SERVER_ROOT=web/
# PHPの本番設定を有効化
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
# プロジェクトのPHPファイルをpublicディレクトリにコピー
COPY . /app/public
# Symfony や Laravel を使用している場合は、代わりにプロジェクト全体をコピーする必要があります:
#COPY . /app
```
より詳細な情報やカスタマイズ方法、PHP拡張モジュールやCaddyモジュールのインストール方法については、
「[カスタムDockerイメージのビルド](docker.md)」を参照してください。
プロジェクトでComposerを使用している場合は、
DockerイメージにComposerを含め、依存関係をインストールしてください。
次に、 `compose.yaml` ファイルを追加します:
```yaml
services:
php:
image: dunglas/frankenphp
restart: always
ports:
- "80:80" # HTTP
- "443:443" # HTTPS
- "443:443/udp" # HTTP/3
volumes:
- caddy_data:/data
- caddy_config:/config
# Caddyの証明書と設定に必要なボリューム
volumes:
caddy_data:
caddy_config:
```
> [!NOTE]
>
> 上記の例は本番環境向けです。
> 開発環境では、ボリューム、異なるPHP設定、`SERVER_NAME`環境変数の異なる値を使用したい場合があります。
>
> [Symfony Docker](https://github.com/dunglas/symfony-docker)プロジェクトFrankenPHPを使用では、
> マルチステージイメージ、Composer、追加のPHP拡張モジュールなどを活用した、
> より高度な例を見ることができます。
最後に、Gitを使用している場合は、これらのファイルをコミットしてプッシュします。
## サーバーの準備
本番環境にアプリケーションをデプロイするには、サーバーが必要です。
このチュートリアルではDigitalOceanの仮想マシンを使用しますが、他のLinuxサーバーでも同様に動作します。
DockerがインストールされたLinuxサーバーが既にある場合は、[次のセクション](#ドメイン名の設定)に進んでください。
まだサーバーがない場合は、[このアフィリエイトリンク](https://m.do.co/c/5d8aabe3ab80)を使用して$200の無料クレジットを取得し、アカウントを作成してください。その後、「Create a Droplet」をクリックします。
次に、「Choose an image」セクションの下の「Marketplace」タブをクリックし、「Docker」という名前のアプリを検索します。
これにより、DockerとDocker Composeの最新バージョンが既にインストールされたUbuntuサーバーがプロビジョニングされます
テスト目的であれば、最安のプランで十分です。
実際の本番使用では、おそらくニーズに合わせて「general purpose」セクションのプランを選びたいでしょう。
![FrankenPHPをDockerでDigitalOceanにデプロイ](digitalocean-droplet.png)
他の設定はデフォルトのままにするか、必要に応じて調整も可能です。
SSHキーを追加するかパスワードを作成することを忘れずに行い、「Finalize and create」ボタンをクリックしてください。
次に、Dropletがプロビジョニングされるまで数秒待ちます。
Dropletの準備ができたら、SSHを使用して接続します
```console
ssh root@<droplet-ip>
```
## ドメイン名の設定
ほとんどの場合、サイトにドメイン名を関連付けたいでしょう。
まだドメイン名を所有していない場合は、レジストラーを通じて購入する必要があります。
次に、サーバーのIPアドレスを指すドメイン名のタイプ`A`のDNSレコードを作成します
```dns
your-domain-name.example.com. IN A 207.154.233.113
```
DigitalOceanのドメインサービス「Networking」 > 「Domains」での例
![DigitalOceanでのDNS設定](digitalocean-dns.png)
> [!NOTE]
>
> FrankenPHPがデフォルトで使用しているTLS証明書の自動生成サービスLet's Encryptは、IPアドレスの直接使用をサポートしていません。Let's Encryptを使用するにはドメイン名の使用が必須です。
## デプロイ
`git clone``scp`など、目的に合ったツールを使用してプロジェクトをサーバーにコピーします。
GitHubを使用している場合は、[deploy key](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys)の使用を検討してください。
deploy keyは[GitLabでもサポートされています](https://docs.gitlab.com/ee/user/project/deploy_keys/)。
Gitでの例
```console
git clone git@github.com:<username>/<project-name>.git
```
プロジェクトディレクトリ(`<project-name>`)に移動し、本番モードでアプリを開始します:
```console
docker compose up --wait
```
サーバーが起動し、HTTPS証明書が自動的に生成されます。
`https://your-domain-name.example.com`にアクセスしてお楽しみください!
> [!CAUTION]
>
> Dockerはキャッシュレイヤーを持つ可能性があるため、各デプロイメントで正しいビルドを持っているか確認するか、キャッシュの問題を避けるために`--no-cache`オプションでプロジェクトを再ビルドしてください。
## 複数ノードへのデプロイ
複数のマシンクラスターにアプリをデプロイしたい場合は、提供されるComposeファイルと互換性のある[Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/)を
使用できます。
Kubernetesでデプロイするには、FrankenPHPを使用する[API Platformで提供されるHelmチャート](https://api-platform.com/docs/deployment/kubernetes/)をご覧ください。

161
docs/ja/static.md Normal file
View File

@@ -0,0 +1,161 @@
# 静的ビルドの作成
PHPライブラリのローカルインストールを使用する代わりに、
[static-php-cli プロジェクト](https://github.com/crazywhalecc/static-php-cli)を利用して、FrankenPHPの静的またはほぼ静的なビルドを作成することが可能ですプロジェクト名に「CLI」とありますが、CLIだけでなく全てのSAPIをサポートしています
この方法を使えば、PHPインタープリター、Caddy Webサーバー、FrankenPHPをすべて含んだ単一でポータブルなバイナリを作成できます
完全に静的なネイティブ実行ファイルは依存関係を全く必要とせず、[`scratch` Dockerイメージ](https://docs.docker.com/build/building/base-images/#create-a-minimal-base-image-using-scratch)上でも実行可能です。
ただし、動的PHP拡張モジュールXdebugなどをロードできず、musl libcを使用しているため、いくつかの制限があります。
ほぼ静的なバイナリは`glibc`のみを必要とし、動的拡張モジュールをロードできます。
可能であれば、glibcベースのほぼ静的ビルドの使用をお勧めします。
また、FrankenPHPは[静的バイナリへのPHPアプリの埋め込み](embed.md)もサポートしています。
## Linux
静的なLinuxバイナリをビルドするためのDockerイメージを提供しています
### muslベースの完全静的ビルド
依存関係なしにあらゆるLinuxディストリビューションで動作する完全静的バイナリただし拡張モジュールの動的ロードはサポートしないを作成するには、以下を実行します
```console
docker buildx bake --load static-builder-musl
docker cp $(docker create --name static-builder-musl dunglas/frankenphp:static-builder-musl):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-musl
```
高い並行性が求められるシナリオでは、より良いパフォーマンスのため、[mimalloc](https://github.com/microsoft/mimalloc)アロケーターの使用を検討してください。
```console
docker buildx bake --load --set static-builder-musl.args.MIMALLOC=1 static-builder-musl
```
### glibcベースのほぼ静的なビルド動的拡張モジュールのサポートあり
選択した拡張モジュールを静的にコンパイルしながら、さらにPHP拡張モジュールを動的にロードできるバイナリを作成するには、以下を実行します
```console
docker buildx bake --load static-builder-gnu
docker cp $(docker create --name static-builder-gnu dunglas/frankenphp:static-builder-gnu):/go/src/app/dist/frankenphp-linux-$(uname -m) frankenphp ; docker rm static-builder-gnu
```
このバイナリは、glibcバージョン2.17以上をすべてサポートしますが、muslベースシステムAlpine Linuxなどでは動作しません。
生成されたほぼ静的(`glibc`を除く)バイナリは`frankenphp`という名前で、カレントディレクトリに出力されます。
Dockerを使わずに静的バイナリをビルドしたい場合は、macOS向けの手順を参照してください。これらの手順はLinuxでも使用できます。
### カスタム拡張モジュール
デフォルトでは、よく使われるPHP拡張モジュールがコンパイルされます。
バイナリのサイズを削減したり、攻撃対象領域(アタックサーフェス)を減らすために、`PHP_EXTENSIONS`というDocker引数を使用してビルドする拡張モジュールを明示的に指定できます。
例えば、`opcache``pdo_sqlite`拡張モジュールのみをビルドするには、以下のように実行します:
```console
docker buildx bake --load --set static-builder-musl.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder-musl
# ...
```
有効にした拡張に必要なライブラリを追加するには、`PHP_EXTENSION_LIBS`というDocker引数を渡すことができます
```console
docker buildx bake \
--load \
--set static-builder-musl.args.PHP_EXTENSIONS=gd \
--set static-builder-musl.args.PHP_EXTENSION_LIBS=libjpeg,libwebp \
static-builder-musl
```
### 追加のCaddyモジュール
Caddyの拡張モジュールを追加したい場合は、`XCADDY_ARGS`というDocker引数を使用して、[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数を以下のように指定できます:
```console
docker buildx bake \
--load \
--set static-builder-musl.args.XCADDY_ARGS="--with github.com/darkweak/souin/plugins/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy" \
static-builder-musl
```
この例では、Caddy用の[Souin](https://souin.io)HTTPキャッシュモジュールと[cbrotli](https://github.com/dunglas/caddy-cbrotli)、[Mercure](https://mercure.rocks)、[Vulcain](https://vulcain.rocks)モジュールを追加しています。
> [!TIP]
>
> cbrotli、Mercure、Vulcainモジュールは、`XCADDY_ARGS`が空または設定されていない場合はデフォルトで含まれます。
> `XCADDY_ARGS`の値をカスタマイズする場合、デフォルトのモジュールは含まれなくなるため、必要なものは明示的に記述してください。
[ビルドのカスタマイズ](#ビルドのカスタマイズ)も参照してください
### GitHubトークン
GitHub API レート制限に達した場合は、`GITHUB_TOKEN`という名前の環境変数にGitHub Personal Access Tokenを設定してください
```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder-musl
# ...
```
## macOS
macOS用の静的バイナリを作成するには以下のスクリプトを実行してください[Homebrew](https://brew.sh/)がインストールされている必要があります):
```console
git clone https://github.com/php/frankenphp
cd frankenphp
./build-static.sh
```
なお、このスクリプトはLinuxおそらく他のUnix系OSでも動作し、私たちが提供するDockerイメージ内部でも使用されています。
## ビルドのカスタマイズ
以下の環境変数を`docker build``build-static.sh`
スクリプトに渡すことで、静的ビルドをカスタマイズできます:
- `FRANKENPHP_VERSION`: 使用するFrankenPHPのバージョン
- `PHP_VERSION`: 使用するPHPのバージョン
- `PHP_EXTENSIONS`: ビルドするPHP拡張[サポートされる拡張のリスト](https://static-php.dev/en/guide/extensions.html)
- `PHP_EXTENSION_LIBS`: 拡張モジュールに追加機能を持たせるためにビルドする追加ライブラリ
- `XCADDY_ARGS`: 追加のCaddyモジュールを導入するなど[xcaddy](https://github.com/caddyserver/xcaddy)に渡す引数
- `EMBED`: バイナリに埋め込むPHPアプリケーションのパス
- `CLEAN`: 指定するとlibphpおよびそのすべての依存関係がスクラッチからビルドされますキャッシュなし
- `NO_COMPRESS`: UPXを使用して結果のバイナリを圧縮しない
- `DEBUG_SYMBOLS`: 指定すると、デバッグシンボルが除去されず、バイナリに含まれます
- `MIMALLOC`: 実験的、Linuxのみパフォーマンス向上のためにmuslのmallocngを[mimalloc](https://github.com/microsoft/mimalloc)に置き換えます。muslをターゲットとするビルドにのみこれを使用することをお勧めします。glibcの場合は、このオプションを無効にして、代わりにバイナリを実行する際に[`LD_PRELOAD`](https://microsoft.github.io/mimalloc/overrides.html)を使用することをお勧めします。
- `RELEASE`: メンテナー用指定すると、生成されたバイナリがGitHubにアップロードされます
## 拡張モジュール
glibcまたはmacOSベースのバイナリでは、PHP拡張モジュールを動的にロードできます。ただし、これらの拡張はZTSサポートでコンパイルされている必要があります。
ほとんどのパッケージマネージャーは現在、拡張のZTSバージョンを提供していないため、自分でコンパイルする必要があります。
このために、`static-builder-gnu`Dockerコンテナをビルドして実行し、リモートでアクセスし、`./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config`で拡張をコンパイルできます。
[Xdebug拡張モジュール](https://xdebug.org)の場合:
```console
docker build -t gnu-ext -f static-builder-gnu.Dockerfile --build-arg FRANKENPHP_VERSION=1.0 .
docker create --name static-builder-gnu -it gnu-ext /bin/sh
docker start static-builder-gnu
docker exec -it static-builder-gnu /bin/sh
cd /go/src/app/dist/static-php-cli/buildroot/bin
git clone https://github.com/xdebug/xdebug.git && cd xdebug
source scl_source enable devtoolset-10
../phpize
./configure --with-php-config=/go/src/app/dist/static-php-cli/buildroot/bin/php-config
make
exit
docker cp static-builder-gnu:/go/src/app/dist/static-php-cli/buildroot/bin/xdebug/modules/xdebug.so xdebug-zts.so
docker cp static-builder-gnu:/go/src/app/dist/frankenphp-linux-$(uname -m) ./frankenphp
docker stop static-builder-gnu
docker rm static-builder-gnu
docker rmi gnu-ext
```
これにより、現在のディレクトリに`frankenphp``xdebug-zts.so`が作成されます。
`xdebug-zts.so`を拡張ディレクトリに移動し、php.iniに`zend_extension=xdebug-zts.so`を追加してFrankenPHPを実行すると、Xdebugがロードされます。

181
docs/ja/worker.md Normal file
View File

@@ -0,0 +1,181 @@
# FrankenPHPワーカーの使用
アプリケーションを一度起動してメモリに保持します。
FrankenPHPは数ミリ秒で受信リクエストを処理します。
## ワーカースクリプトの開始
### Docker
`FRANKENPHP_CONFIG`環境変数の値を`worker /path/to/your/worker/script.php`に設定します:
```console
docker run \
-e FRANKENPHP_CONFIG="worker /app/path/to/your/worker/script.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### スタンドアロンバイナリ
`php-server`コマンドの`--worker`オプションを使って、現在のディレクトリのコンテンツをワーカーを通じて提供できます:
```console
frankenphp php-server --worker /path/to/your/worker/script.php
```
PHPアプリが[バイナリに埋め込まれている](embed.md)場合は、アプリのルートディレクトリにカスタムの`Caddyfile`を追加することができます。
これが自動的に使用されます。
また、`--watch`オプションを使えば、[ファイルの変更に応じてワーカーを再起動](config.md#watching-for-file-changes)することも可能です。
以下のコマンドは、`/path/to/your/app/`ディレクトリおよびそのサブディレクトリ内の`.php`で終わるファイルが変更された場合に再起動をトリガーします:
```console
frankenphp php-server --worker /path/to/your/worker/script.php --watch="/path/to/your/app/**/*.php"
```
## Symfonyランタイム
FrankenPHPのワーカーモードは[Symfony Runtime Component](https://symfony.com/doc/current/components/runtime.html)によってサポートされています。
ワーカーでSymfonyアプリケーションを開始するには、FrankenPHP用の[PHP Runtime](https://github.com/php-runtime/runtime)パッケージをインストールします:
```console
composer require runtime/frankenphp-symfony
```
アプリケーションサーバーを起動するには、FrankenPHP Symfony Runtimeを使用するように`APP_RUNTIME`環境変数を定義します:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-e APP_RUNTIME=Runtime\\FrankenPhpSymfony\\Runtime \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
## Laravel Octane
[専用ドキュメント](laravel.md#laravel-octane)を参照してください。
## カスタムアプリ
以下の例は、サードパーティライブラリに依存せずに独自のワーカースクリプトを作成する方法を示しています:
```php
<?php
// public/index.php
// クライアント接続が中断されたときのワーカースクリプト終了を防ぐ
ignore_user_abort(true);
// アプリを起動
require __DIR__.'/vendor/autoload.php';
$myApp = new \App\Kernel();
$myApp->boot();
// ループの外側にハンドラーを配置してパフォーマンスを向上(処理量を減らす)
$handler = static function () use ($myApp) {
// リクエストを受信した際に呼び出され、
// スーパーグローバルや php://input などがリセットされます。
echo $myApp->handle($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
};
$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0);
for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) {
$keepRunning = \frankenphp_handle_request($handler);
// HTTPレスポンスの送信後に何か処理を行います
$myApp->terminate();
// ページ生成の途中でガベージコレクタが起動する可能性を減らすために、ここでガベージコレクタを明示的に呼び出す。
gc_collect_cycles();
if (!$keepRunning) break;
}
// クリーンアップ
$myApp->shutdown();
```
次に、アプリを開始し、`FRANKENPHP_CONFIG`環境変数を使用してワーカーを設定します:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
デフォルトでは、CPU当たり2つのワーカーが開始されます。
開始するワーカー数を設定することもできます:
```console
docker run \
-e FRANKENPHP_CONFIG="worker ./public/index.php 42" \
-v $PWD:/app \
-p 80:80 -p 443:443 -p 443:443/udp \
dunglas/frankenphp
```
### 一定数のリクエスト処理後にワーカーを再起動する
PHPはもともと長時間実行されるプロセス向けに設計されていなかったため、メモリリークを引き起こすライブラリやレガシーコードがいまだに多く存在します。
こうしたコードをワーカーモードで利用するための回避策として、一定数のリクエストを処理した後にワーカースクリプトを再起動する方法があります:
前述のワーカー用スニペットでは、`MAX_REQUESTS`という名前の環境変数を設定することで、処理する最大リクエスト数を設定できます。
### ワーカーの手動再起動
[ファイルの変更を監視](config.md#watching-for-file-changes)してワーカーを再起動することも可能ですが、
[Caddy admin API](https://caddyserver.com/docs/api)を使用してすべてのワーカーをグレースフルに安全に再起動することも可能です。adminが
[Caddyfile](config.md#caddyfile-config)で有効になっている場合、次のような単純なPOSTリクエストで再起動エンドポイントにpingできます
```console
curl -X POST http://localhost:2019/frankenphp/workers/restart
```
### ワーカーの失敗
ワーカースクリプトがゼロ以外の終了コードでクラッシュした場合、FrankenPHP は指数的バックオフ戦略を用いて再起動を行います。
ワーカースクリプトが最後のバックオフ時間 × 2 より長く稼働し続けた場合、
それ以降の再起動ではペナルティを科しません。
しかし、スクリプトにタイプミスがあるなど短時間で何度もゼロ以外の終了コードで失敗し続ける場合、
FrankenPHP は`too many consecutive failures`というエラーとともにクラッシュします。
連続失敗の回数上限は、[Caddyfile](config.md#caddyfile-config)の`max_consecutive_failures`オプションで設定できます:
```caddyfile
frankenphp {
worker {
# ...
max_consecutive_failures 10
}
}
```
## スーパーグローバルの動作
[PHPのスーパーグローバル](https://www.php.net/manual/en/language.variables.superglobals.php)`$_SERVER``$_ENV``$_GET`など)
は以下のように動作します:
- `frankenphp_handle_request()`が最初に呼び出される前は、スーパーグローバルにはワーカースクリプト自体にバインドされた値が格納されています
- `frankenphp_handle_request()`の呼び出し中および呼び出し後は、スーパーグローバルには処理されたHTTPリクエストから生成された値が格納され、`frankenphp_handle_request()`を呼び出すたびにスーパーグローバルの値が変更されます
コールバック内でワーカースクリプトのスーパーグローバルにアクセスするには、それらをコピーしてコールバックのスコープにコピーをインポートする必要があります:
```php
<?php
// frankenphp_handle_request()を最初に呼び出す前に、ワーカーの $_SERVER スーパーグローバルをコピー
$workerServer = $_SERVER;
$handler = static function () use ($workerServer) {
var_dump($_SERVER); // リクエストにバインドされた $_SERVER
var_dump($workerServer); // ワーカースクリプトの $_SERVER
};
// ...
```

71
docs/ja/x-sendfile.md Normal file
View File

@@ -0,0 +1,71 @@
# 大きな静的ファイルを効率的に配信する `X-Sendfile`/`X-Accel-Redirect`
通常、静的ファイルはウェブサーバーによって直接配信されますが、
時にはファイルを送信する前にPHPコードを実行する必要があります。
例えば、アクセス制御、統計、カスタムHTTPヘッダーなど
残念ながら、PHPを使用して大きな静的ファイルを配信することは、
ウェブサーバーを直接使うより非効率的です(メモリ過負荷、パフォーマンス低下など)。
FrankenPHPでは、カスタマイズされたPHPコードを実行した**後**に、
静的ファイルの送信をウェブサーバーに委譲できます。
この機能を使うには、PHPアプリケーションは提供するファイルのパスを含む
カスタムHTTPヘッダーを定義するだけです。残りの処理はFrankenPHPが行います。
この機能は、Apacheでは **`X-Sendfile`** 、NGINXでは **`X-Accel-Redirect`** として知られています。
以下の例では、プロジェクトのドキュメントルートが`public/`ディレクトリであり、
`public/`ディレクトリの外部に保存されたファイルを
`private-files/`ディレクトリからPHPで提供したいと仮定します。
## 設定方法
まず、この機能を有効にするために以下の設定を`Caddyfile`に追加します:
```patch
root public/
# ...
+ # Symfony や Laravel など、Symfony HttpFoundation コンポーネントを使用するプロジェクトに必要
+ request_header X-Sendfile-Type x-accel-redirect
+ request_header X-Accel-Mapping ../private-files=/private-files
+
+ intercept {
+ @accel header X-Accel-Redirect *
+ handle_response @accel {
+ root private-files/
+ rewrite * {resp.header.X-Accel-Redirect}
+ method * GET
+
+ # セキュリティ強化のため、 PHP によって設定された X-Accel-Redirect ヘッダーを削除
+ header -X-Accel-Redirect
+
+ file_server
+ }
+ }
php_server
```
## プレーンなPHPの場合
`private-files/`からの相対パスを`X-Accel-Redirect`ヘッダーの値として設定します:
```php
header('X-Accel-Redirect: file.txt');
```
## Symfony HttpFoundationコンポーネントを使用するプロジェクトの場合Symfony、Laravel、Drupalなど
SymfonyのHttpFoundationは[この機能をネイティブサポート](https://symfony.com/doc/current/components/http_foundation.html#serving-files)しており、
`X-Accel-Redirect`ヘッダーの正しい値を自動的に決定してレスポンスに追加します。
```php
use Symfony\Component\HttpFoundation\BinaryFileResponse;
BinaryFileResponse::trustXSendfileTypeHeader();
$response = new BinaryFileResponse(__DIR__.'/../private-files/file.txt');
// ...
```