Goで書かれたPHPアプリケーションサーバーのFrankenPHPを触ってみた

![スクリーンショット 2023-12-19 0.43.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/765794/3317b5d1-125c-60b4-9637-e522a76e57a4.png)
Goで書かれたPHPアプリケーションサーバーである[FrankenPHP](https://frankenphp.dev/)を触ってみました。
2023/12/11にバージョン1.0.0がリリースされたPHPアプリケーションサーバーです。やっと触れました。

https://frankenphp.dev/

## FrankenPHPとは
[公式](https://frankenphp.dev/docs/)より
> FrankenPHP is a modern application server for PHP built on top of the Caddy web server.

*FrankenPHPは、Caddy Webサーバー(※1)の上に構築されたPHP用のモダンなアプリケーションサーバーです。*

> FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: Early Hints, worker mode, real-time capabilities, automatic HTTPS, HTTP/2, and HTTP/3 support…

*FrankenPHPは素晴らしい機能で、あなたのPHPアプリケーションにスーパーパワーを与えます： アーリーヒント(※2)、ワーカーモード、リアルタイム機能、自動HTTPS、HTTP/2、HTTP/3のサポート*

> FrankenPHP works with any PHP app and makes your Symfony projects faster than ever thanks to the provided integration with the worker mode (Laravel Octane support coming).

*FrankenPHPはどのようなPHPアプリでも動作し、ワーカーモードとの統合によりSymfonyプロジェクトをこれまで以上に高速化します。*
（LaravelのOctaneサポートも予定されているようです。）

> FrankenPHP can also be used as a standalone Go library to embed PHP in any app using net/http.

*FrankenPHPは、スタンドアローンのGoライブラリとしても使用でき、net/httpを使用するアプリケーションにPHPを組み込むことができます。*

### 補足
**※1 Goで実装されたWEBサーバー**

公式

https://caddyserver.com/

**※2 HTTP Status Code 103 Early Hints とはWebの読み込みを30%以上高速化させることが期待されている新しいWeb標準のステータスコード**

おすすめブログ

https://zenn.dev/kameoncloud/articles/6d2dec59232917

## ネイティブサポート
主要なフレームワークやCMSに対応しています。
- Laravel
- Symfony
- WordPress
- Drupal
etc.

## FrankenPHPの機能
- PHP 8.2以降のほとんどのPHP機能拡張、およびすべてのCaddyモジュールと互換性を持っている
- アプリケーションを起動しメモリに保持しておくワーカーモード
- 1つのサービス、1つのバイナリだけでPHP-FPMを必要としない（Nginxも不要）。独自のSAPIを利用
- PHPアプリをスタンドアロンで自己実行可能な静的バイナリとしてパッケージ化することも可
- Early Hintsをサポートする唯一のPHP SAPI
- Mercure ハブを内蔵し、イベントをWebブラウザへ送信、JavaScriptイベントとしてペイロードを受け取れる
- ZstandardおよびGzip圧縮
- 構造化されたログ
- HTTPS・HTTP/2・HTTP/3のネイティブサポート
- Prometheusサポート
- HTTPS証明書の自動生成や更新
- グレースフル・リロードにより、ダウンタイムなしでアプリをデプロイ可能

## Let's try
今回は[公式の手順](https://frankenphp.dev/docs/)に従って**Docker**での起動を試してみます。
Dockerfileで書きたい、もっと拡張したいなどは[公式のdocker.md](https://github.com/dunglas/frankenphp/blob/main/docs/docker.md)見ると良さそうです。

```zsh
docker run -v $PWD:/app/public \
    -p 80:80 -p 443:443 \
    dunglas/frankenphp
```
:::note warn
バインドのローカルパスは`$PWD`が指定されているので、コマンド実行する時にお気をつけください。
`$PWD:/app/public`
:::

ログを見るとPHPのバージョンは`8.3.0`が利用されています。（ログはJSONになってます）
```
{"level":"info","ts":1702945366.1317804,"msg":"FrankenPHP started 🐘","php_version":"8.3.0"}
```

公式に従って`https://localhost`にアクセスします。
>Go to https://localhost, and enjoy!

そのままアクセスすると案の定、、、
![スクリーンショット 2023-12-19 9.27.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/765794/d5c4c4a1-f804-20bf-0476-29b7f38c503e.png)
![スクリーンショット 2023-12-19 9.27.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/765794/76946864-a2d1-790d-f6c8-2f7c3353b62f.png)

しっかりエラーログも確認できました。何もないので404となったようです。
```
ERROR   http.log.access.log0    handled request {"request": {"remote_ip": "***.***.***.***", "remote_port": "*****", "client_ip": "***.***.***.***", "proto": "HTTP/2.0", "method": "GET", "host": "localhost", "uri": "/", "headers": {"Accept": ["text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7"], "Sec-Fetch-Dest": ["document"], "Sec-Ch-Ua-Mobile": ["?0"], "Sec-Fetch-Mode": ["navigate"], "Accept-Language": ["ja"], "Sec-Fetch-User": ["?1"], "Cache-Control": ["max-age=0"], "Dnt": ["1"], "Upgrade-Insecure-Requests": ["1"], "Sec-Fetch-Site": ["none"], "Sec-Ch-Ua": ["\"Not_A Brand\";v=\"8\", \"Chromium\";v=\"120\", \"Google Chrome\";v=\"120\""], "Sec-Ch-Ua-Platform": ["\"macOS\""], "User-Agent": ["Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"], "Accept-Encoding": ["gzip, deflate, br"]}, "tls": {"resumed": false, "version": 772, "cipher_suite": 4865, "proto": "h2", "server_name": "localhost"}}, "bytes_read": 0, "user_id": "", "duration": 0.001238042, "size": 0, "status": 404, "resp_headers": {"Server": ["Caddy"], "Alt-Svc": ["h3=\":443\"; ma=2592000"]}}
```

## Dockerコンテナの中を探検
※ Docker Desktopのダッシュボードを利用しています。

OSはDebian bookworm、カーネルは6.4.16-linuxkitが使われています。
```
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

# cat /proc/version
Linux version 6.4.16-linuxkit (root@buildkitsandbox) (gcc (Alpine 12.2.1_git20220924-r10) 12.2.1 20220924, GNU ld (GNU Binutils) 2.40) #1 SMP PREEMPT Thu Nov 16 10:49:20 UTC 2023

# openssl version
OpenSSL 3.0.11 19 Sep 2023 (Library: OpenSSL 3.0.11 19 Sep 2023)
```

PHPのバージョンは上述のように8.3.0が利用されていて、PDOをなどをはじめとしてPHP拡張モジュールも多く入っています。

```
# php -v
PHP 8.3.0 (cli) (built: Dec 16 2023 02:04:34) (ZTS)
Copyright (c) The PHP Group
Zend Engine v4.3.0, Copyright (c) Zend Technologies

# php -m
[PHP Modules]
Core
ctype
curl
date
dom
fileinfo
filter
ftp
hash
iconv
json
libxml
mbstring
mysqlnd
openssl
pcre
PDO
pdo_sqlite
Phar
posix
random
readline
Reflection
session
SimpleXML
sodium
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
zlib

[Zend Modules]
```

## 何か表示させてみる！！
コンテナの`/app/public`に.phpファイルを置いてみました。
```
# pwd
/app/public
# ls
example.php
# cat example.php
<?php
echo "Hello FrankenPHP!!";
```
表示されました。
![スクリーンショット 2023-12-19 10.03.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/765794/8f24cb79-d4f6-4741-698e-7cde7b35e6a8.png)

## おわりに
なかなか時間がなくDockerコンテナを起動させるだけに留まってしました。
（これだとただPHPコンテナ起動したのと同じじゃん。。。）
近いうちに[Static Build](https://frankenphp.dev/docs/static/)とLaravelのアプリケーションをFrankenPHPを使って作成してみようと思います。（Laravelはすぐできそう）
Caddyも深掘りしてみたいし、個人的にはEarly Hintsもちゃんと勉強しようと思いました。キャッチアップ大変だけど、面白いですね😄

## 一緒に働いてくれる仲間募集中
GoQSystemでは一緒に働いてくれるPHPエンジニアを募集中です！

https://goqsystem.recruit.site/requirements.html