【Homebrew】brew upgradeしたらPHP7.4が使えなくなった

# 目的
``brew upgrade``の実行後にPHP7.4が使えなくなりました。解決に苦労したので、その手順を書き残しておきます。

裏技的な方法なのであまり推奨はできませんが、参考になれば幸いです。

# 動作環境
- MacBook Air M1 2020
- macOS Ventura 13.2.1
- Homebrew 3.5.4
- PHPはHomebrewのパッケージ``php@7.4``を使用

```bash
% php -v
PHP 7.4.30 (cli) (built: Jun  9 2022 09:20:03) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v3.1.4, Copyright (c) 2002-2022, by Derick Rethans
    with Zend OPcache v7.4.30, Copyright (c), by Zend Technologies
% which php
/opt/homebrew/opt/php@7.4/bin/php
```

# Homebrewのアップグレード
Homebrewのバージョンを``3.5.4``から``4.0.6``へアップグレードします。今回は``update``ではなく``upgrade``を使用し、パッケージの更新も合わせて実行しました。

```bash
% brew -v
Homebrew 3.5.4
Homebrew/homebrew-core (git revision 6ffb2a2c25e; last commit 2022-07-18)
```

エラーが発生したので、メッセージの指示通り``xcode-select``のインストールを実行します。
```bash
% brew upgrade
Error: No developer tools installed.
Install the Command Line Tools:
  xcode-select --install
```

コマンドを実行するとポップアップが表示されるので「インストール」をクリックします。
```bash
% xcode-select --install
xcode-select: note: install requested for command line developer tools
```
![スクリーンショット 2023-03-12 17.15.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654760/4c1382a5-abcd-40c9-9c6a-4c3ce1afb8d0.png)
「ソフトウェアがインストールされました。」が表示されれば、xcode-selectのインストールは完了です。

再度brew upgradeを実行します。時間がかかるので気長に待ちます。
```bash
% brew upgrade
```

# PHP7.4が使えなくなった
``php -v``を実行したところ、エラーが発生しました。``icu4c``ライブラリの参照で失敗しているようです。参照先のパスから``icu4c``はHomebrewのパッケージと分かります。
```
% php -v
dyld[69930]: Library not loaded: /opt/homebrew/opt/icu4c/lib/libicuio.70.dylib
  Referenced from: <31116583-6CB3-3C7B-AD46-52FF1431267E> /opt/homebrew/Cellar/php@7.4/7.4.30/bin/php
  Reason: tried: '/opt/homebrew/opt/icu4c/lib/libicuio.70.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/opt/icu4c/lib/libicuio.70.dylib' (no such file), '/opt/homebrew/opt/icu4c/lib/libicuio.70.dylib' (no such file), '/usr/local/lib/libicuio.70.dylib' (no such file), '/usr/lib/libicuio.70.dylib' (no such file, not in dyld cache), '/opt/homebrew/Cellar/icu4c/72.1/lib/libicuio.70.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/Cellar/icu4c/72.1/lib/libicuio.70.dylib' (no such file), '/opt/homebrew/Cellar/icu4c/72.1/lib/libicuio.70.dylib' (no such file), '/usr/local/lib/libicuio.70.dylib' (no such file), '/usr/lib/libicuio.70.dylib' (no such file, not in dyld cache)
zsh: abort      php -v
```

参照先である``/opt/homebrew/opt/icu4c/lib/``を確認します。エラーメッセージにあった``libicuio.70.dylib``ではなく、``libicuio.72.dylib``が配置されています。
```bash
% ls /opt/homebrew/opt/icu4c/lib/
icu			libicudata.dylib	libicui18n.dylib	libicuio.dylib		libicutest.dylib	libicutu.dylib		libicuuc.dylib
libicudata.72.1.dylib	libicui18n.72.1.dylib	libicuio.72.1.dylib	libicutest.72.1.dylib	libicutu.72.1.dylib	libicuuc.72.1.dylib	pkgconfig
libicudata.72.dylib	libicui18n.72.dylib	libicuio.72.dylib	libicutest.72.dylib	libicutu.72.dylib	libicuuc.72.dylib
libicudata.a		libicui18n.a		libicuio.a		libicutest.a		libicutu.a		libicuuc.a
```

# icu4cの設定を確認
``brew info``から、icu4cの現在のバージョンは``72.1``であると確認できます。Poured from~の時刻がbrew upgradeと同じタイミングだったので、upgradeの影響を受けたようです。また、``/opt/homebrew/opt/icu4c/lib/libicuio.72.dylib``の``72``はicu4cのバージョンと思われます。
```bash
% brew info icu4c
==> icu4c: stable 72.1 (bottled) [keg-only]
C/C++ and Java libraries for Unicode and globalization
https://icu.unicode.org/home
/opt/homebrew/Cellar/icu4c/72.1 (263 files, 78.4MB) # 現在のバージョン
  Poured from bottle using the formulae.brew.sh API on 2023-03-12 at 17:28:50
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/icu4c.rb
License: ICU
# 以下省略
```

今回のエラーは、brew upgradeによりicu4cが``72系``に更新されたことが原因のようです。PHP7.4は``70系``を使用するため、このままでは参照エラーが発生します。

# icu4cのダウングレード
icu4cを70系に戻します。具体的には、icu4cの``Formulae（インストール方法などを含むパッケージの定義書）``を``git checkout``で70系に切り替えてicu4cを再インストールします。

## icu4cのFormulaeを探す
``brew --config``の``HOMEBREW_PREFIX``に記述されているディレクトリの配下で``Formula``ディレクトリを検索します。
```bash
% brew --config | grep HOMEBREW_PREFIX
HOMEBREW_PREFIX: /opt/homebrew
```

```bash
 % find /opt/homebrew -name Formula -type d
/opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula
```

Formulaディレクトリへ移動します。
```bash
% cd /opt/homebrew/Library/Taps/homebrew/homebrew-core/Formula
```

切り替え対象のFormula``icu4c.rb``を見つけました。
```bash
% ls | grep icu4c
icu4c.rb
```

## icu4cのFormulaeを70系に戻す
``git log``でicu4c.rbのコミット履歴を確認し、70系の最新コミットを探します。参考に、``brew log  --oneline icu4c``でもほぼ同じ内容が表示されます。 
```bash
% git log --oneline icu4c.rb
11249c583b5 icu4c: update 72.1 bottle.
025d9d1deaf icu4c 72.1
e3317b86c11 icu4c: update 71.1 bottle.
54fb3277728 icu4c: update 71.1 bottle.
c013b416f31 icu4c: update homepage url
97427b4fde6 icu4c: update 71.1 bottle.
9010e85a7cb icu4c 71.1                 # ここから71系
fe6a2fe48c2 icu4c: update homepage     # このコミットに戻す
aa997d1c221 icu4c 70.1
```

[公式サイト](https://formulae.brew.sh/formula/icu4c#default)のリンクからログを追うことも可能です。
>Formula code: icu4c.rb on GitHub

https://github.com/Homebrew/homebrew-core/blob/09fa6f218b2aed79455adc419017763c2b7a081f/Formula/icu4c.rb

70系の最新コミットは``fe6a2fe48c2``です。icu4c.rbをこのコミットにチェックアウトします。
```bash
% git checkout fe6a2fe48c2 icu4c.rb
Updated 1 path from bbfb6dca4b4
```

## 72系のicu4cをアンインストール
再インストール前に72系のicu4cをアンインストールします。
```bash
% brew uninstall icu4c
```

## 70系のicu4cをインストール
※Formulaeの名前によりインストールされるパッケージのバージョンが異なりますが、挙動の理由は不明です。

70系のicu4cをインストールします。Formulaeは``icu4c.rb``を指定してください。``icu4c``を指定した場合は70系ではなく72系のisu4cがインストールされます。
```bash
% HOMEBREW_NO_AUTO_UPDATE=1 brew install icu4c.rb
```

実行後は70系のみインストールされると思いきや、``72.1``と``70.1``の2つがインストールされています。
```bash
% brew list icu4c --versions
icu4c 72.1 70.1
```

## icu4cのFormulaeを元に戻す
インストール後はicu4c.rbを元のコミットへ戻します。
```bash
% git reset --hard
```

## 再度72系のicu4cをアンインストール
再度72系のicu4cをアンインストールします。依存関係を無視するため``--ignore-dependencies``オプションを追加します。ここで70系もアンインストールされると思いきや、70系は対象外のようです。
```bash
% brew uninstall --ignore-dependencies icu4c
Uninstalling /opt/homebrew/Cellar/icu4c/72.1... (263 files, 78.4MB)
icu4c 70.1 is still installed. # 70系はアンインストールされない
To remove all versions, run:
  brew uninstall --force icu4c
% brew list icu4c --versions
icu4c 70.1
```

## 70系のicu4cを反映
``brew link``を実行し``icu4c 70.1``のパッケージを反映します。``~/.zshrc``の追記は不要です。
```bash
% brew link icu4c --force
Linking /opt/homebrew/Cellar/icu4c/70.1... 63 symlinks created.

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/opt/homebrew/opt/icu4c/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/opt/homebrew/opt/icu4c/sbin:$PATH"' >> ~/.zshrc
```

icu4cの参照エラーが解決し、再びPHP7.4が使えるようになりました！
```bash
% php -v
PHP 7.4.30 (cli) (built: Jun  9 2022 09:20:03) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Xdebug v3.1.4, Copyright (c) 2002-2022, by Derick Rethans
    with Zend OPcache v7.4.30, Copyright (c), by Zend Technologies
```

# PHP8.2ではエラーが発生する
brew upgradeによりデフォルトのPHPのパッケージが8.2系に更新されました。そのため``~/.zshrc``からPHP7.4の設定を削除した場合はPHP8.2が参照されます。
```.zshrc
# PHP7.4の設定を削除
export PATH="/opt/homebrew/opt/php@7.4/bin:$PATH"
export PATH="/opt/homebrew/opt/php@7.4/sbin:$PATH"
```

しかし、PHP8.2ではエラーが発生します。PHP8.2系ではicu4cの``72系``が使われているためです。
```bash
% php -v
dyld[45528]: Library not loaded: /opt/homebrew/opt/icu4c/lib/libicuio.72.dylib
  Referenced from: <3680E42B-1530-312F-8F4F-42D652FCEA9B> /opt/homebrew/Cellar/php/8.2.3/bin/php
  Reason: tried: '/opt/homebrew/opt/icu4c/lib/libicuio.72.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/opt/icu4c/lib/libicuio.72.dylib' (no such file), '/opt/homebrew/opt/icu4c/lib/libicuio.72.dylib' (no such file), '/usr/local/lib/libicuio.72.dylib' (no such file), '/usr/lib/libicuio.72.dylib' (no such file, not in dyld cache), '/opt/homebrew/Cellar/icu4c/70.1/lib/libicuio.72.dylib' (no such file), '/System/Volumes/Preboot/Cryptexes/OS/opt/homebrew/Cellar/icu4c/70.1/lib/libicuio.72.dylib' (no such file), '/opt/homebrew/Cellar/icu4c/70.1/lib/libicuio.72.dylib' (no such file), '/usr/local/lib/libicuio.72.dylib' (no such file), '/usr/lib/libicuio.72.dylib' (no such file, not in dyld cache)
zsh: abort      php -v
```

解決の方法は[こちらの記事](https://qiita.com/y_sone/private/e53cdd62c826edc14172)で説明しています。

# パッケージのバージョンは切り替え可能か
``brew switch``でパッケージのバージョン切り替え行う方法も見つけましたが、自分の環境ではエラーが発生し利用できませんでした。
```bash
# バージョンを自由に切り替えたかった...
% brew list icu4c --versions
icu4c 72.1 70.1
# コマンド自体が利用できない
% brew help switch
Error: Unknown command: switch
```

似た事例を見つけましたが、原因は分かりませんでした。

https://github.com/rd2/tbd/issues/58

# 参考記事
https://www.ibm.com/docs/ja/aix/7.1?topic=programs-icu4c-libraries

https://tech.noricgeographic.com/xcode%E3%81%A8xcode-select%E3%81%A8command-line-tools-for-xcode/

https://www.tweeeety.blog/entry/2018/05/04/181928

https://qiita.com/KyoheiG3/items/912bcc27462871487845

https://rcmdnk.com/blog/2022/11/26/computer-homebrew-mac/

# 採用PR
弊社で一緒に働く仲間を募集しています。

Devトークやってます。お気軽にご応募ください(^^)

https://plus.jobs.qiita.com/dev-talk-company-pickup-vivion/

募集要項はこちらをご覧ください！

https://hrmos.co/pages/vivion/jobs?category=1687770395095928832