🧟‍♂️

FrankenPHPでLaravelを動かしてみよう

に公開

この記事は下記スライドの補足資料的な立ち位置です(環境構築の説明に振り切っています)
https://speakerdeck.com/yousaku/frankenphpdelaravelwodong-kasitemiyou

1.モチベーション

試してみたいよね、FrankenPHP。
みんなが好きな?Laravelを動かしたいよね。

2.注意事項

今回は最小構成です。
既存Laravelプロジェクトが全部FrankenPHPで動くとは限りません。ご了承ください。

3.環境紹介

PC

  • Apple M4
  • Seqoia 15.5
  • メモリ16GB

開発ツール、プログラミング言語

  • Docker version 28.1.1
  • Docker Compose version v2.35.1-desktop.1
  • PHP 8.4.8
  • Composer 2.8.9
  • Laravel 12.0

4.FrankenPHPとは

https://frankenphp.dev

5.まずは試してみる

とにかくインストール

curl https://frankenphp.dev/install.sh | sh
mv frankenphp /usr/local/bin/
sudo mv /Users/{ユーザー名}/frankenphp /usr/local/bin/
frankenphp --version

homebrewを使ってインストールも可能ですが、自分は既存のPHPとバッティングして面倒だったのでしませんでした。※何回か試していたらhomebrewでも上手くいきました。

brew install dunglas/frankenphp/frankenphp

https://github.com/dunglas/homebrew-frankenphp

Hello World

ひとまず簡単に動かしてみましょう

franenphp-hello-world
└── public
    └── index.php

webサーバー起動

frankenphp php-server -r public
# Caddyが動いてlocalhostで確認できます

command line App起動

frankenphp php-cli public/index.php
# Hello, FrankenPHP!が出力されます

6.今回作る構成

DockerでLaravelアプリが動く最小構成を作ってみよう

  • Laravel(FrankenPHP)
  • DB(SQLite, MySQL, PostreSQL)
@startuml
!define RECTANGLE class

actor Client as "Client\n(ブラウザ / API Client)"

rectangle "Laravel Application\n(FrankenPHP)" as Laravel
database "Database\n(SQLite / MySQL /  PostgreSQL)" as DB

Client --> Laravel : HTTP Request
Laravel --> DB : DB Query
Laravel --> Client : HTTP Response

@enduml

仮想環境を使わずにLaravelプロジェクトをFrankenPHPで動かす

Laravelプロジェクト作成

今回は作ります

composer create-project laravel/laravel frankenphp-laravel-sample

Laravelプロジェクト起動

cd frankenphp-laravel-sample
php artisan serve

動いた。

FrankenPHPを導入していく

laravel/octaneを導入しましょう。
https://github.com/laravel/octane

octaneのおかげでいろんな設定をよしなにやってくれます。

laravel/octaneをインストール

composer require laravel/octane

設定

php artisan octane:install --server=frankenphp

FrankenPHPでLaravelプロジェクトを起動

php artisan octane:frankenphp

同じ画面が表示されます。

frankenphpのバイナリーがないと言われるのでダウンロードしましょう

FrankenPHPで動かす(Laravel Sail編)

Sailでも使えます。
https://laravel.com/docs/12.x/octane

Sailを導入

https://laravel.com/docs/12.x/sail

laravel/octane導入

公式ドキュメントを参考にしてください。
https://laravel.com/docs/12.x/octane#frankenphp-via-laravel-sail

docker-compose.ymlの変更を忘れずに

docker-compose.yml
services:
    laravel.test:
        build:
            context: './vendor/laravel/sail/runtimes/8.4'
            dockerfile: Dockerfile
            args:
                WWWGROUP: '${WWWGROUP}'
        image: 'sail-8.4/app'
        extra_hosts:
            - 'host.docker.internal:host-gateway'
        ports:
            - '${APP_PORT:-80}:80'
            - '${VITE_PORT:-5173}:${VITE_PORT:-5173}'
        environment:
            # 環境変数を変えて起動し直すと切り替わります
            # cli-server
            # WWWUSER: '${WWWUSER}'
            # LARAVEL_SAIL: 1
            # XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}'
            # XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}'
            # IGNITION_LOCAL_SITES_PATH: '${PWD}'

            # frankenphp
            SUPERVISOR_PHP_COMMAND: "/usr/bin/php -d variables_order=EGPCS /var/www/html/artisan octane:start --server=frankenphp --host=0.0.0.0 --admin-port=2019 --port='${APP_PORT:-80}'" 
            XDG_CONFIG_HOME:  /var/www/html/config 
            XDG_DATA_HOME:  /var/www/html/data 

        volumes:
            - '.:/var/www/html'
        networks:
            - sail
        depends_on:
            - mysql
    mysql:
        image: 'mysql/mysql-server:8.0'
        ports:
            - '${FORWARD_DB_PORT:-3306}:3306'
        environment:
            MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ROOT_HOST: '%'
            MYSQL_DATABASE: '${DB_DATABASE}'
            MYSQL_USER: '${DB_USERNAME}'
            MYSQL_PASSWORD: '${DB_PASSWORD}'
            MYSQL_ALLOW_EMPTY_PASSWORD: 1
        volumes:
            - 'sail-mysql:/var/lib/mysql'
            - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
        networks:
            - sail
        healthcheck:
            test:
                - CMD
                - mysqladmin
                - ping
                - '-p${DB_PASSWORD}'
            retries: 3
            timeout: 5s
networks:
    sail:
        driver: bridge
volumes:
    sail-mysql:
        driver: local

http://127.0.0.1にアクセスしましょう

FrankenPHPで動かす(Docker編)

Laravel Sailを使わずにコンテナを立てます。公式Docにある情報を元に構築します。
https://laravel.com/docs/12.x/octane#frankenphp-via-docker

今回はホストのディレクトりをバインドマウントします。これで開発環境としてcomposerでパッケージをinstallしてもvendor配下が確認できます。

プロジェクトにdocker-compose.yml作成

docker-compose.yml
services:
  app:
    build:
      context: .
      dockerfile: ./docker/php/Dockerfile
    ports:
      - "8000:8000"
  db:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: develop
    volumes:
      - frankenphp-laravel-sample-db:/var/lib/mysql
volumes:
  frankenphp-laravel-sample-db:

プロジェクトにdocker/Dockerfiledocker/entrypoint.shを作成

今回は時間がなかったのでシェルスクリプトを駆使して無理やりマウントしました。もっと良い書き方があったかもしれません。

Dockerfile
FROM dunglas/frankenphp

# composer を追加
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 必要な PHP 拡張をインストール
RUN install-php-extensions \
    pcntl \
    pdo_mysql \
    pdo_pgsql \ 
    pgsql \
    gd \
    intl \
    zip \
    opcache

WORKDIR /app

# Laravel プロジェクトをコピー
COPY . /app

# entrypoint.sh をコンテナ実行パスにコピー
COPY docker/php/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# composer 環境変数を設定
ENV COMPOSER_ALLOW_SUPERUSER=1

# CMD でシェルスクリプトを実行
CMD ["entrypoint.sh"]
entrypoint.sh
#!/bin/sh

# vendor が存在しない場合のみ composer install
if [ ! -d "vendor" ]; then
    echo "Running composer install..."
    composer install
fi

# APP_KEY が設定されていない場合のみ key:generate を実行
if ! grep -q '^APP_KEY=base64:' .env; then
    echo "Generating app key..."
    php artisan key:generate
fi

# FrankenPHP起動
exec php artisan octane:frankenphp

7.デプロイしてみよう

fly.ioを使ってデプロイします。
https://fly.io

事前準備

fly.ioのアカウント作成とflyctlの準備をしておきましょう。
https://fly.io/docs/flyctl/install

自分の環境

fly v0.3.144

Dockerfile作成

プロジェクトルートにDockerfileを用意します。
シェルスクリプトを使わずCMDでFrankenPHPを起動するようなDockerfileを書きます。
今回はLaravel Sailを使わないDocker環境を使います。

Dockerfile
FROM dunglas/frankenphp

# composer を追加
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

# 必要な PHP 拡張をインストール
RUN install-php-extensions \
    pcntl \
    pdo_pgsql \ 
    pgsql \
    gd \
    intl \
    zip \
    opcache

WORKDIR /app

# Laravel プロジェクトをコピー
COPY . /app

# composer 環境変数を設定
ENV COMPOSER_ALLOW_SUPERUSER=1

RUN composer install --no-dev --optimize-autoloader

ENTRYPOINT ["php", "artisan", "octane:frankenphp"]

最終的なディレクトリ構成

frankenphp-laravel-sample
├── app
├── artisan
├── bootstrap
├── composer.json
├── composer.lock
├── config
├── database
├── docker
├── docker-compose.yml
├── Dockerfile ///// このDockerfileが必要です
├── fly.toml
├── Makefile
├── package.json
├── phpunit.xml
├── public
├── README.md
├── resources
├── routes
├── storage
├── tests
├── vendor
└── vite.config.js

flyctlでプロジェクト初期化

CLIで実行

fly launch (Do you want to tweak these settings before proceeding? Yesで回答)

Fly.io側の設定

自動で画面遷移をしてインフラの詳細設定を求められたので下記を入力しました。

◯Basics
App name : frankenphp-laravel-sample
Organization : Personal

◯Region
Tokyo

◯Postgres
Provider : Fly Unmanaged Postgres
Name : frankenphp-laravel-sample-db
Configuration : Development

◯Redis
none

◯Sentry
Disables

自動生成されたものを少し手直しします。

fly.toml
app = '自分が作ったApp名'
primary_region = 'nrt'
console_command = 'php /var/www/html/artisan tinker'

[build]
  [build.args]
    NODE_VERSION = '18'
    PHP_VERSION = '8.2'

[env]
  APP_ENV = 'production'
  LOG_CHANNEL = 'stderr'
  LOG_LEVEL = 'info'
  LOG_STDERR_FORMATTER = 'Monolog\Formatter\JsonFormatter'
  SESSION_DRIVER = 'cookie'
  SESSION_SECURE_COOKIE = 'true'
  DB_CONNECTION = 'pgsql'
  DB_HOST = 'postgresql://自分が作ったdb名.flycast'
  DB_DATABASE= '自分が作ったApp名' 
  DB_PORT = 5432

[http_service]
  internal_port = 8000
  force_https = true
  auto_stop_machines = 'stop'
  auto_start_machines = true
  min_machines_running = 0
  processes = ['app']

[[vm]]
  memory = '1gb'
  cpu_kind = 'shared'
  cpus = 1

デプロイ実施

fly deploy

cli側がプロジェクトを検知し勝手にデプロイを行ってくれます(これすごい)。

マイグレーション

DBのマイグレーションを忘れずに
※「デプロイ時に自動でマイグレーションできる」という記事を見つけたのですが上手くいきませんでした。自分の勘違いかも知れないので何か知ってる方がいたら教えてください。
https://fly.io/docs/laravel/database-guides/laravel-db-migrations/

コマンドで仮想マシン内に入りartisanコマンドをいつも通り動かしましょう

fly ssh console
---- fly.ioのコンソール内 ---
php artisan migrate:status
php artisan migrate
exit

8.まだまだ試したいことがたくさんある

Caddyfileのカスタムもできてない。キューを利用した機能も組んでない。
FrankenPHP、おもちゃとしてのポテンシャルが高い。

9.今回のコード

https://github.com/You-saku/phpconference2025_lt

10.参考文献

FrankenPHPやLaravel関連は丁寧にドキュメントが用意されています。いつくか紹介しておきます。Fly.ioについても紹介します。
https://frankenphp.dev/docs/docker
https://frankenphp.dev/docs/laravel
https://laravel.com/docs/12.x/octane
https://laravel.com/docs/12.x/sail
https://fly.io/docs/getting-started/launch
https://fly.io/docs/laravel/database-guides/laravel-pgsql
https://fly.io/docs/laravel/database-guides/laravel-db-migrations

深掘りたい場合は作者が出してる記事や動画がおすすめです
https://dunglas.dev/2022/10/frankenphp-the-modern-php-app-server-written-in-go

Discussion