phpenv install で発生したエラーと解消方法

# はじめに
- 普段、PHPを書くことが多いのですがプロダクトによってPHPのバージョンが異なるので簡単にバージョンを切り替えられるようにphpenvのセットアップを行いました
- `phpenv install`でPHPをインストール時、`エラー → 解消 → エラー　→ 解消 → ...`をひたすら繰り返しかなり疲弊しました...（特に7.x系）。ので、インストール成功までに実施したことをマシン交換時などに困らないようここに書き留めておきます

## 環境
- macOS Monterey 12.7
- Apple M1
- zsh 5.8.1 (x86_64-apple-darwin21.0)
- anyenv経由でphpenvインストール済み

## PHP7.3.33インストール時に発生したエラーと解決方法
- 環境変数のオプション指定について
    - `~/.anyenv/envs/phpenv/plugins/php-build/share/php-build`配下にある`default_configure_options`というファイルに記述する方法もあるみたいなのですが、記述しても認識してくれないオプションがあったのでコマンド実行時に直接指定してしまうのが確実です
    - `zlib`と`bzip2`については上記ファイルに記述することで認識してくれたようなのでコマンド実行時にオプション指定は行いませんでした
    - ちなみに記述したオプション以下２つ
        - `--with-zlib-dir=$(brew --prefix zlib)`
        - `--with-bz2=$(brew --prefix bzip2)`
- 1 ~ 8のエラーを上から順番に解消していきました
    - ビルドに成功した最終的な環境変数付きの実行コマンドは`8.`の最後に記述してあります

### 1. configure: error: jpeglib.h not found
- 確認したこと
  - `jpeg（libjpeg）`がインストールされているか → されていた
      - `brew info jpeg`
- 試したこと
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-jpeg-dir`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="--with-jpeg-dir=$(brew --prefix jpeg)" phpenv install 7.3.33
        ```
        
### 2. configure: error: png.h not found.
- 確認したこと
    - `libpng`がインストールされているか → されていた
        - `brew info libping`
- 試したこと
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-png-dir`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping)" phpenv install 7.3.33
        ```
        
### 3. configure: error: Please specify the install prefix of iconv with --with-iconv=DIR
- 確認したこと
    - `libiconv`がインストールされているか → されていた
        - `brew info libiconv`
- 試したこと
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-iconv`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv)" phpenv install 7.3.33
        ```
        
### 4. configure: error: Cannot find libtidy
- 確認したこと
    - `libtidy(tidy-html5)`がインストールされているか → されていた
        - `brew info libtidy`
        - **Error: No available formula with the name "libtidy"**
            - 調べてみるとbrewでは`tidy-html5`が入っていれば良いらしい
        - `brew info tidy-html5`
            - 入っていた
- 試したこと
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-tidy`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv) \
        --with-tidy=$(brew --prefix tidy-html5)" phpenv install 7.3.33
        ```
        
### 5. opensslに関するエラー
<details><summary>エラーログで問題となっていた箇所抜粋</summary>

- `RSA_SSLV23_PADDING`がビルドの時に使っているOpenSSL3.0では削除もしくは非推奨となっているのでエラーになってしまっている模様
- 
    ```text
    /var/tmp/php-build/source/7.3.33/ext/openssl/openssl.c:1485:51: error: use of undeclared identifier 'RSA_SSLV23_PADDING'
                    REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING", RSA_SSLV23_PADDING, CONST_CS|CONST_PERSISTENT);
    ```
</details>

- 試したこと
    - OpenSSL@1.1をインストール
        - `brew install openssl@1.1`
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-openssl`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv) \
        --with-tidy=$(brew --prefix tidy-html5) \
        --with-openssl=$(brew --prefix openssl@1.1)" phpenv install 7.3.33
        ```
    
### 6. libxml2に関するエラー
<details>
    <summary>エラーログの一部を抜粋</summary>

- ビルド時に利用している`libxml2`に`xmlSaveNoEmptyTags`が宣言されていないためエラーとなっている模様
    - brewにインストールされていた`libxml2`は`2.12.0`
    - 
        ```text
        /var/tmp/php-build/source/7.3.33/ext/dom/document.c:1659:4: error: use of undeclared identifier 'xmlSaveNoEmptyTags'
                                xmlSaveNoEmptyTags = 1;
                                ^
        /var/tmp/php-build/source/7.3.33/ext/dom/document.c:1664:4: error: use of undeclared identifier 'xmlSaveNoEmptyTags'
                                xmlSaveNoEmptyTags = saveempty;
                                ^
        9 errors generated.
        make: *** [ext/dom/document.lo] Error 1
        make: *** Waiting for unfinished jobs....
        ```
</details>

- 試したこと
    - 古いバージョン(今回は`2.9.4`)の`libxml2`をインストール
        - wgetで`2.9.4`の`libxml2`をダウンロード&ビルド
        - 
            ```zsh
            wget http://xmlsoft.org/sources/libxml2-2.9.4.tar.gz
            tar xzvf libxml2-2.9.4.tar.gz
            cd libxml2-2.9.4
            ./configure --prefix={任意のフォルダパス}
            make
            make install
            ```
        - `5.`のmakeコマンドでエラー
        -
            ```text
            /Applications/Xcode.app/Contents/Developer/usr/bin/make  all-recursive
            Making all in include
            Making all in libxml
            make[3]: Nothing to be done for `all'.
            make[3]: Nothing to be done for `all-am'.
            Making all in .
              CC       xmlIO.lo
            xmlIO.c:1450:52: error: use of undeclared identifier 'LZMA_OK'
                ret =  (__libxml2_xzclose((xzFile) context) == LZMA_OK ) ? 0 : -1;
                                                               ^
            1 error generated.
            make[2]: *** [xmlIO.lo] Error 1
            make[1]: *** [all-recursive] Error 1
            make: *** [all] Error 2
            13:24:50 [ libxml2-2.9.4 ] %
            ```
        - `4.` ./configureを実行する際に以下のように環境変数を指定して実行
        - 
            ```zsh
            LDFLAGS="-L$(brew --prefix xz)/lib" CPPFLAGS="-I$(brew --prefix xz)/include" ./configure --prefix={任意のフォルダパス}
            ```
        - これらの手順でlibxml2の`2.9.4`のインストールが完了
        
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-libxml-dir`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv) \
        --with-tidy=$(brew --prefix tidy-html5) \
        --with-openssl=$(brew --prefix openssl@1.1) \
        --with-libxml-dir={2.9.4をインストールしたフォルダパス}" phpenv install 7.3.33
        ```

### 7. icu4cに関するエラー
<details>
    <summary>エラーログ抜粋</summary>
    
- PHP7.3のintl拡張がbrewにインストールされているicu4cのバージョンと互換性が無いのでエラーが出ている模様
    - brewに入っていたicu4cのバージョンは`73.2`だった
    - 
        ```text
        In file included from /var/tmp/php-build/source/7.3.33/ext/intl/breakiterator/breakiterator_class.cpp:23:
        /var/tmp/php-build/source/7.3.33/ext/intl/breakiterator/codepointiterator_internal.h:42:17: error: virtual function 'operator==' has a different return type ('UBool' (aka 'signed char')) than the function it overrides (which has return type 'bool')
                        virtual UBool operator==(const BreakIterator& that) const;
                                ~~~~~ ^
        /opt/homebrew/Cellar/icu4c/73.2/include/unicode/brkiter.h:127:18: note: overridden virtual function is here
            virtual bool operator==(const BreakIterator&) const = 0;
                    ~~~~ ^
        1 error generated.
        make: *** [ext/intl/breakiterator/breakiterator_class.lo] Error 1
        make: *** Waiting for unfinished jobs.
        ```
</details>

- 試したこと
    - 古いバージョンのicu4cをインストールする（今回は64.2）
        - brewで任意のバージョンをインストールする方法があったので以下を実行
        - 
            ```zsh
            brew tap-new icu4c-prev/taps
            brew extract icu4c icu4c-prev/taps --version 64.2
            brew extract icu4c icu4c-prev/taps --version 64
            brew tap homebrew/core
            brew extract icu4c icu4c-prev/taps --version 64.2
            brew install icu4c-prev/taps/icu4c@64.2
            xcode-select --install #インストールを求められたので実行
            brew install icu4c-prev/taps/icu4c@64.2
            # icu4c@64.2がリストに追加されているはず
            brew list
            ```
    -  環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-icu-dir`オプションを指定
    - 
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv) \
        --with-tidy=$(brew --prefix tidy-html5) \
        --with-openssl=$(brew --prefix openssl@1.1) \
        --with-libxml-dir={2.9.4をインストールしたフォルダパス}　\
        --with-icu-dir=$(brew --prefix icu4c@64.2" phpenv install 7.3.33
        ```

### 8.pcre関連のエラー
<details>
    <summary>エラーログの一部を抜粋</summary>
    
- バンドルされているpcreを使うと何故か以下のようなエラーが発生する模様
    - pearでPHP_Archiveをインストールすれば良いのかと思っていたけど違いました
        ```text
        /var/tmp/php-build/source/7.3.33/sapi/phpdbg/phpdbg_out.c:879:15: warning: passing an object that undergoes default argument promotion to 'va_start' has undefined behavior [-Wvarargs]
                va_start(va, escape_xml);
                             ^
        /var/tmp/php-build/source/7.3.33/sapi/phpdbg/phpdbg_out.c:875:79: note: parameter of type 'zend_bool' (aka 'unsigned char') is declared here
        PHPDBG_API int _phpdbg_xml_asprintf(char **buf, const char *format, zend_bool escape_xml, ...) {
                                                                                      ^
        1 warning generated.
        PEAR package PHP_Archive not installed: generated phar will require PHP's phar extension be enabled.
        make: *** [ext/phar/phar.phar] Bus error: 10
         ```
 
</details>

- 確認したこと
    -  `pcre2`がインストールされているか → されていた
        -  `brew info pcre2`
- 試したこと
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-external-pcre`オプションを指定
        - エラーは解消されず内容も上記と同じまま
    - 環境変数`PHP_BUILD_CONFIGURE_OPTS`に`--with-pcre-regex`オプションを指定
        - こちらが正解でした
        - PHP7.3までは外部のpcreを利用する場合はこのオプションが有効らしい
            - ちなみに7.4からはこのオプションが消えて代わりとして`--with-external-pcre`が追加された模様
            - 参考: https://www.php.net/manual/ja/migration74.other-changes.php
    - 完成
        ```zsh
        PHP_BUILD_CONFIGURE_OPTS="\ 
        --with-jpeg-dir=$(brew --prefix jpeg) \
        --with-png-dir=$(brew --prefix libping) \
        --with-iconv=$(brew --prefix libiconv) \
        --with-tidy=$(brew --prefix tidy-html5) \
        --with-openssl=$(brew --prefix openssl@1.1) \
        --with-libxml-dir={2.9.4をインストールしたフォルダパス}　\
        --with-icu-dir=$(brew --prefix icu4c@64.2 \
        --with-pcre-regex=$(brew --prefix pcre2)" phpenv install 7.3.33
        ```

## PHP8.2.12インストール時に発生したエラーと解決方法
- PHP8.2.12に関しては新しいバージョンなので7.3.33の時より発生するエラーがかなり少なかったです
    - opensslやlibxmlの古いバージョンを使う必要もなくインストールに成功しました
- 後述するコマンドを実行する前に`.zshrc`に以下を記述
    - `export PKG_CONFIG_PATH=/opt/homebrew/opt/jpeg/lib/pkgconfig`
    - 自分の場合は上記のみでインストールに成功しましたが人によっては他にもexportする必要があるかもしれないです
- 
    ```zsh
    PHP_BUILD_CONFIGURE_OPTS="\
    --with-iconv=$(brew --prefix libiconv) \
    --with-tidy=$(brew --prefix tidy-html5)" phpenv install 8.2.12
    ```
    >[Success]: Built 8.2.1 successfully.

## さいごに
- brewの`new-tap`と`extract`コマンドは今回のセットアップで初めて知ったのでちょっとした収穫でした
- **To: 未来の自分**
    - 故障等でマシンの交換になった時、この記事を未来の自分が見てPHPのセットアップがスムーズに出来ることを祈っています:innocent: 
    - 今回出なかったエラーが来たらphpenv使うのはやめて[shivammathur/homebrew-php](https://github.com/shivammathur/homebrew-php)を利用してください:metal: