カスタムビルドしたPHPをGitHub Releaseにアップロードしてmiseで使う

mise(ミーズ)、プロジェクトディレクトリごとに別々の言語環境をセットアップするのに便利ですが PHP環境をセットアップするのは次のような不便な点があります。

  • mise use php@xxx コマンドをした時点でビルドする(数分かかる)
  • mise.tomlpostinstallを設定すれば追加のPHP拡張インストールなどもできますが、同じPHPバージョンで異なる構成にする方法は不明

GitHub Release を使うことで、Configure option やPHP拡張をカスタマイズしたパッケージをアップロードし、 ネットワークが高速なら数秒でインストールできるPHPを用意できます。 プライベートリポジトリで社内向けにPHPバイナリを用意することもできそうです。

PHPをビルド・パッケージ化してGitHub ReleaseにアップロードするGitHub Actions

以下のようなファイルをGitHubリポジトリに用意します。

# .github/workflows/build-php.yml
name: Build PHP

run-name: "Build PHP ${{ inputs.php_version }}"

on:
  workflow_dispatch:
    inputs:
      php_version:
        required: true
        default: "8.5.4"

permissions:
  contents: write

jobs:
  resolve-tag:
    runs-on: ubuntu-24.04
    outputs:
      tag: ${{ steps.resolve.outputs.tag }}
    steps:
      - uses: actions/checkout@v6
      - name: Resolve next build number
        id: resolve
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          PREFIX="custom-php-v${{ inputs.php_version }}"
          # List existing tags matching the prefix, extract build numbers
          EXISTING=$(gh api repos/${{ github.repository }}/git/matching-refs/tags/"$PREFIX" --jq '.[].ref' 2>/dev/null || true)
          NEXT=0
          if [ -n "$EXISTING" ]; then
            # Extract the highest build number from tags like refs/tags/custom-php-v8.5.4.N
            MAX=$(echo "$EXISTING" | sed "s|refs/tags/${PREFIX}\.||" | sort -n | tail -1)
            if [ -n "$MAX" ] && [ "$MAX" -eq "$MAX" ] 2>/dev/null; then
              NEXT=$((MAX + 1))
            fi
          fi
          TAG="${PREFIX}.${NEXT}"
          echo "tag=$TAG" >> "$GITHUB_OUTPUT"
          echo "Resolved tag: $TAG"

  build:
    needs: resolve-tag
    strategy:
      matrix:
        os: [ubuntu-24.04, macos-15]
    runs-on: ${{ matrix.os }}
    env:
      INSTALL_PATH: /opt/mise/data/installs/github-tenkoma-mise-php-build/custom-php-v${{ inputs.php_version }}

    steps:
      - name: export PATH
        run: echo "PATH=$INSTALL_PATH/bin:$PATH" >> "$GITHUB_ENV"

      - uses: actions/checkout@v6

      # PHP をビルドするときの --prefix パスは利用するときのパスと同一にしたほうがよい
      - name: Create install directories
        run: |
          sudo mkdir -p /opt/mise/data && sudo chown -R "$(id -u):$(id -g)" /opt/mise/data
          mkdir -p $(dirname "$INSTALL_PATH")

      - name: Install build dependencies (Ubuntu)
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y re2c libxml2-dev libsqlite3-dev \
            libcurl4-openssl-dev libonig-dev libreadline-dev libzip-dev \
            libpng-dev libgd-dev libpq-dev libicu-dev libgettextpo-dev \
            libtidy-dev libxslt1-dev

      - name: Install build dependencies (macOS)
        if: runner.os == 'macOS'
        run: |
          brew install autoconf re2c bison pkg-config libpng gd icu4c libpq gettext libzip libiconv bzip2 webp tidy-html5 libxslt

      - name: Checkout php-build
        run: git clone --depth 1 https://github.com/php-build/php-build.git

      - name: Build PHP (Ubuntu)
        if: runner.os == 'Linux'
        env:
          CFLAGS: "-std=gnu17"
          CXXFLAGS: "-std=c++17"
          PHP_BUILD_CONFIGURE_OPTS: "--with-gettext --with-webp"
        run: |
          ./php-build/bin/php-build -v ${{ inputs.php_version }} "$INSTALL_PATH"

      - name: Build PHP (macOS)
        if: runner.os == 'macOS'
        run: |
          export CFLAGS="-std=gnu17"
          export CXXFLAGS="-std=c++17 -stdlib=libc++ -DU_USING_ICU_NAMESPACE=1"
          export PHP_BUILD_CONFIGURE_OPTS="\
            --with-bz2=$(brew --prefix bzip2) \
            --with-iconv=$(brew --prefix libiconv) \
            --with-gettext=$(brew --prefix gettext) \
            --with-libzip=$(brew --prefix libzip) \
            --with-webp=$(brew --prefix webp) \
            --with-zlib=$(brew --prefix zlib) \
            --with-tidy=$(brew --prefix tidy-html5)"
          ./php-build/bin/php-build -v ${{ inputs.php_version }} "$INSTALL_PATH"

      - name: Install PHP extensions via pie
        run: |
          pie install apcu/apcu
          pie install phpredis/phpredis

      - name: Package
        run: |
          VERSION=${{ inputs.php_version }}
          OS=$(uname -s | tr '[:upper:]' '[:lower:]')
          ARCH=$(uname -m)
          tar -czf "custom-php-v${VERSION}-${OS}-${ARCH}.tar.gz" \
            -C "$INSTALL_PATH" .

      - name: Upload to Release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          TAG="${{ needs.resolve-tag.outputs.tag }}"
          # リリースが存在しなければ作成
          if ! gh release view "$TAG" > /dev/null 2>&1; then
            gh release create "$TAG" --title "$TAG" --notes "PHP ${{ inputs.php_version }} custom build"
          fi
          gh release upload "$TAG" custom-php-v*.tar.gz --clobber

Actions > Build PHP で使いたいPHPバージョンを入力して “Run workflow” を押します。

ビルド成功すると、GitHub Release にパッケージファイルが登録されています。

ここまでは mise と直接関係はありません。 PHPパッケージをアップロードするリポジトリを分けたり、タグのプレフィックスを使い分けることで同じPHPバージョンだけどビルドオプションの異なるPHPを用意できます。

ビルドしたPHPバイナリをプロジェクトで使う。

miseドキュメントにかかれているセットアップ手順は省略しますが、 ここでビルドしたPHPを利用するためには、追加の準備が必要です。

  1. mise がツールをダウンロード&インストールするディレクトリをつくる
sudo mkdir -p /opt/mise/data && sudo chown -R "$(id -u):$(id -g)" /opt/mise/data

PHPビルド時に指定された --prefix={パス} と同じディレクトリにインストールする必要があるので同じディレクトリを作ります。

  1. MISE_DATA_DIR 環境変数を ~/.zshrc に追記する
# mise
export MISE_DATA_DIR=/opt/mise/data

これで事前準備は終わりです。プロジェクトでPHPをインストールするときは以下の mise.toml を保存します。

[settings.github]
# PHPパッケージをダウンロードしたいGitHub Releaseがプライベートリポジトリの場合に必要
use_git_credentials = true

[tools]
# "github:{リポジトリ名}" = "{タグ}" 形式で
"github:tenkoma/mise-php-build" = "custom-php-v8.5"

mise trust && mise install すれば、プロジェクトディレクトリ以下にいるとき専用のPHPバージョンに切り替わります。

cd path/to/project_dir
mise trust
mise install

これでセットアップ完了です。

resolve-tag job で、バージョン番号を追加

ビルドするごとに custom-php-v8.5.4.0, custom-php-v8.5.4.1, custom-php-v8.5.4.2 とReleaseを追加します。 そうすると、 mise install で一番バージョン番号が大きいものを使いますし、 mise upgrade で更新できます。

参考ドキュメント

2026/04/01 12:20 更新

  • vfox-php を使う場合openssl@1.1に依存しておりEOLを迎えているので、php-buildを使うよう変更した。
  • resolve-tag jobを追加し、 mise upgrade で更新可能にした。