dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicudata.67.dylibのエラーでPHP7.4が突然使えなくなった話



ある日、突然MacでPHP7.4.9が使えなくなりました😭😭😭
結構ハマりましたが、お戻りになってきてくれました🙌

結論

icu4cが69.1にあがってしまっていた。
→ brewで67.1を入れ直して、シンボリックリンクを貼ればok

エラー内容

$ php --version
dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicudata.67.dylib
Referenced from: /Users/user_xxx/.phpenv/versions/7.4.9/bin/php
Reason: image not found
Abort trap: 6

とりあえず/usr/local/opt/icu4c/lib/libicudata.67.dylibが本当にないのか、
/usr/local/opt/icu4c/lib/以下を確認。
→現在のシンボリックが69.1しかないので、67のシンボリックリンクを作りたい。。。🤤

$ ll /usr/local/opt/icu4c/lib/
total 137024
drwxr-xr-x 28 user_xxx staff 896 4 8 09:10 .
drwxr-xr-x 12 user_xxx staff 384 5 27 17:27 ..
drwxr-xr-x 6 user_xxx staff 192 4 8 09:10 icu
-r--r--r-- 1 user_xxx staff 28672096 5 27 16:15 libicudata.69.1.dylib
lrwxr-xr-x 1 user_xxx staff 21 4 8 09:10 libicudata.69.dylib -> libicudata.69.1.dylib
-r--r--r-- 1 user_xxx staff 28665232 4 8 09:10 libicudata.a
lrwxr-xr-x 1 user_xxx staff 21 4 8 09:10 libicudata.dylib -> libicudata.69.1.dylib
-rw-r--r-- 1 user_xxx staff 2726984 5 27 16:15 libicui18n.69.1.dylib
lrwxr-xr-x 1 user_xxx staff 21 4 8 09:10 libicui18n.69.dylib -> libicui18n.69.1.dylib
...

1.brewでicu4cの67をいれる。

brew tap-newとかbrew extractを使うやり方をしらなくてハマりました。

$ brew tap-new kin29/taps #「kin29」の部分はなんでもokです
Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/kin29/homebrew-taps/.git/
[main (root-commit) 58a4f09] Create kin29/taps tap
3 files changed, 88 insertions(+)
create mode 100644 .github/workflows/publish.yml
create mode 100644 .github/workflows/tests.yml
create mode 100644 README.md
==> Created kin29/taps
/usr/local/Homebrew/Library/Taps/kin29/homebrew-taps

When a pull request making changes to a formula (or formulae) becomes green
(all checks passed), then you can publish the built bottles.
To do so, label your PR as `pr-pull` and the workflow will be triggered.
$ brew extract icu4c kin29/taps --version 67
==> Searching repository history
==> Writing formula for icu4c from revision e2c833d to:
/usr/local/Homebrew/Library/Taps/kin29/homebrew-taps/Formula/icu4c@67.rb
$ brew install kin29/taps/icu4c@67
==> Installing icu4c@67 from kin29/taps
==> Downloading https://github.com/unicode-org/icu/commit/715d254a02b0b22681cb6f861b0921ae668fa7d6.patch?full_index=1
Already downloaded: /Users/user_xxx/Library/Caches/Homebrew/downloads/0dd175e315a7eee5f9fb42af4e03d2130e3a3aa130db4d2b32a58b1c88d87251--715d254a02b0b22681cb6f861b0921ae668fa7d6.patch
==> Downloading https://github.com/unicode-org/icu/releases/download/release-67-1/icu4c-67_1-src.tgz
Already downloaded: /Users/user_xxx/Library/Caches/Homebrew/downloads/5652b331b5400231692d0879d41522870a20598d8771342b622d543c2ed99ca0--icu4c-67_1-src.tgz
==> Patching
==> Applying 715d254a02b0b22681cb6f861b0921ae668fa7d6.patch
patching file source/common/uassert.h
==> ./configure --prefix=/usr/local/Cellar/icu4c@67/67.1 --disable-samples --disable-tests --enable-static --with-library-bits=64
==> make
==> make install
==> Caveats
icu4c@67 is keg-only, which means it was not symlinked into /usr/local,
because macOS provides libicucore.dylib (but nothing else).

If you need to have icu4c@67 first in your PATH, run:
echo 'export PATH="/usr/local/opt/icu4c@67/bin:$PATH"' >> /Users/user_xxx/.bash_profile
echo 'export PATH="/usr/local/opt/icu4c@67/sbin:$PATH"' >> /Users/user_xxx/.bash_profile

For compilers to find icu4c@67 you may need to set:
export LDFLAGS="-L/usr/local/opt/icu4c@67/lib"
export CPPFLAGS="-I/usr/local/opt/icu4c@67/include"

For pkg-config to find icu4c@67 you may need to set:
export PKG_CONFIG_PATH="/usr/local/opt/icu4c@67/lib/pkgconfig"

==> Summary
🍺 /usr/local/Cellar/icu4c@67/67.1: 258 files, 71.8MB, built in 4 minutes 47 seconds

icu4c@67がインストールされたことを確認!

$ ll /usr/local/Cellar/ | grep icu
drwxr-xr-x 4 user_xxx staff 128 5 27 16:15 icu4c
drwxr-xr-x 3 user_xxx admin 96 5 29 18:04 icu4c@67
$ ll /usr/local/opt/ | grep icu4c
lrwxr-xr-x 1 user_xxx admin 20 5 27 16:15 icu4c -> ../Cellar/icu4c/69.1
lrwxr-xr-x 1 user_xxx admin 23 5 29 18:09 icu4c@67 -> ../Cellar/icu4c@67/67.1 #今回追加分

2.シンボリックリンクを貼る⚠️

ln -s [パス] [リンク名]です。私はパスとリンク名の指定が逆と勘違いしていて、ハマりました💦
(間違えたらunlink [リンク名]で削除できます。)

$ cd /usr/local/opt/icu4c/lib/
$ ln -s ../../../icu4c@67.1/67.1/lib/libicuio.67.1.dylib libicuio.67.dylib
$ ln -s ../../../icu4c@67.1/67.1/lib/libicui18n.67.1.dylib libicui18n.67.dylib
$ ln -s ../../../icu4c@67.1/67.1/lib/libicuuc.67.1.dylib libicuuc.67.dylib
$ ln -s ../../../icu4c@67.1/67.1/lib/libicudata.67.1.dylib libicudata.67.dylib
$ ln -s ../../../icu4c@67.1/67.1/lib/libicutest.67.1.dylib libicutest.67.dylib
$ ln -s ../../../icu4c@67/67.1/lib/libicutu.67.1.dylib libicutu.67.dylib

シンボリックリンクが貼れていることを確認します。

$ ll /usr/local/opt/icu4c/lib/ | grep 67.dy
lrwxr-xr-x 1 user_xxx staff 50 5 29 22:11 libicudata.67.dylib -> ../../../icu4c@67.1/67.1/lib/libicudata.67.1.dylib
lrwxr-xr-x 1 user_xxx staff 50 5 29 22:09 libicui18n.67.dylib -> ../../../icu4c@67.1/67.1/lib/libicui18n.67.1.dylib
lrwxr-xr-x 1 user_xxx staff 48 5 29 22:08 libicuio.67.dylib -> ../../../icu4c@67.1/67.1/lib/libicuio.67.1.dylib
lrwxr-xr-x 1 user_xxx staff 50 5 29 22:09 libicutest.67.dylib -> ../../../icu4c@67.1/67.1/lib/libicutest.67.1.dylib
lrwxr-xr-x 1 user_xxx staff 46 5 29 22:09 libicutu.67.dylib -> ../../../icu4c@67/67.1/lib/libicutu.67.1.dylib
lrwxr-xr-x 1 user_xxx staff 48 5 29 22:09 libicuuc.67.dylib -> ../../../icu4c@67.1/67.1/lib/libicuuc.67.1.dylib

 

PHP7.4お戻りになってくれたー😭😭😭ありがとう🎊

php --version
PHP 7.4.9 (cli) (built: Dec 26 2020 23:21:06) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
with Xdebug v3.0.1, Copyright (c) 2002-2020, by Derick Rethans

 

助けてくれてありがとうございました🙇‍♂️
ref: https://www.tohuandkonsome.site/entry/2021/05/21/173612


「fn」というワードはPHP7.4から予約語に!(→アロー関数)

 

「fn」というワードはPHP7.4から予約語になったというのを知るに至った経緯として、$ symfony serveでローカルサーバを立てて

$ symfony serve
                                                                                                                        
 [OK] Web server listening                                                                                              
      The Web server is using PHP FPM 7.3.11                                                                            
      https://127.0.0.1:8000                                         


[Web Server ] Jan 18 18:48:55 |INFO   | PHP    listening path="/usr/sbin/php-fpm" php="7.3.11" port=61746                                              

 

ブラウザで表示確認をすると、以下の500エラーがでました😱

(phpenvでいろんなPHPバージョンを使ってます。)

HTTP 500 Internal Server Error
syntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM)
ParseError
in /Users/xxx/my-project/vendor/laminas/laminas-code/src/Generator/ClassGenerator.php (line 480)
470    /**
471     * @param string $implementedInterface
472     * @return bool
473     */
474    public function hasImplementedInterface($implementedInterface)
475    {
476        $interfaceType = TypeGenerator::fromTypeString($implementedInterface);
477
478        return (bool) array_filter(
479            array_map([TypeGenerator::class, 'fromTypeString'], $this->implementedInterfaces),
480            static fn (TypeGenerator $interface): bool => $interfaceType->equals($interface)
481        );
482    }

 

よーく確認してみると、
symfony php --versionphp --versionのバージョンが違う」
ことに気づきました💦

$ symfony php --version
PHP 7.3.11 (cli) (built: Jun  5 2020 23:50:40) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.11, Copyright (c) 1998-2018 Zend Technologies

$ php --version
PHP 7.4.9 (cli) (built: Dec 26 2020 23:21:06) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
    with Xdebug v3.0.1, Copyright (c) 2002-2020, by Derick Rethans

 

そこで、$ symfony serve でもPHP 7.4.9で動くように設定します。

参照:https://symfony.com/doc/current/setup/symfony_server.html#different-php-settings-per-project

$ cd my-project/

$ echo 7.4.9 > .php-version

$ symfony php --version # PHP 7.3.11 -> 7.4.9に変更完了
PHP 7.4.9 (cli) (built: Dec 26 2020 23:21:06) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.9, Copyright (c), by Zend Technologies
    with Xdebug v3.0.1, Copyright (c) 2002-2020, by Derick Rethans


$ symfony serve # PHP 7.4.9でサーバ立てることに成功

                                                                                                                        
 [OK] Web server listening                                                                                              
      The Web server is using PHP FPM 7.4.9                                                                             
      https://127.0.0.1:8000                                                                                     

[Web Server ] Jan 18 18:50:59 |INFO   | PHP    listening path="/Users/xxx/.phpenv/versions/7.4.9/sbin/php-fpm" php="7.4.9" port=62271

お! PHP7.4.9でサーバ立てるようになったみたいです!
と同時にsyntax error, unexpected 'fn' (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM)のエラーもなくなりました🎉

めでたし、めでたし👏

 

syntax error, unexpected ‘fn’ (T_STRING), expecting :: (T_PAAMAYIM_NEKUDOTAYIM)の解明

参照:https://www.php.net/manual/ja/tokens.php

 

Google翻訳先生に訳してもらうと、

構文エラー、予期しない '楽しい'(t_string)、予期する::( t_pamaim_nikoodatim)

おそらく fn = 楽しい って訳してくれてます💦

ググっていくと、「fn」はPHP7.4から予約語になったらしいです!

というかアロー関数(無名関数)が使えるようになり、その予約語としてfnを使います。

(風の噂でアロー関数が使えるようになったというのは知っていたのですが書き方まで知らなかったです😅)

アロー関数は fn (argument_list) => expr という形で記述します。

よって、PHP 7.4まではアロー関数がなかったために以下の該当箇所を、「::」を使うスコープ定義演算子 (::)のことではないですか?と教えてくれたのかもしれません。

static fn (TypeGenerator $interface): bool => $interfaceType->equals($interface)

 

phpenvでPHP7.4系を導入した(configure optionをちょっと深ぼる)

 

実行環境は、macOS Catalina 10.15.7 です。

macOS Big Sur にアップデートしたらどうなるんだろうか….🤔迷ってます

 

(過去記事でphpenvでよく使うコマンドまとめてます。)

phpenvでいろんなPHPバージョンを操る

 

しばらくコードを書いておらず、PHPのバージョンが7.3.7だったので7.4系にバージョンあげますー😅

$ php --version
PHP 7.3.7 (cli) (built: Oct  6 2020 15:27:59) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.7, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.3.7, Copyright (c) 1999-2018, by Zend Technologies
    with Xdebug v2.7.2, Copyright (c) 2002-2019, by Derick Rethans

$ phpenv versionsに7.4系が入ってなかったので、

$ phpenv update

からの

$ phpenv install 7.4.9

を実行したら、

-----------------------------------------
configure: error: Please reinstall the BZip2 distribution
-----------------------------------------

といわれたので、
bzip2をreinstallしてパスも通しましたが解決しませんでした。

ググって以下でinstallしてみました。

$ PHP_RPATHS="/usr/local/opt/bzip2/lib" PHP_BUILD_CONFIGURE_OPTS="--with-bz2=/usr/local/opt/bzip2" phpenv install 7.4.9

そしたら、

-----------------------------------------
configure: error: Please specify the install prefix of iconv with --with-iconv=<DIR>
-----------------------------------------

って言われました。どんどん芋釣で問題出てくる、phpenvあるある。
念のため、iconvもreinstallとパスを通して、以下コマンドで再チャレンジ!

 

!!!解決コマンド!!!!

$ PHP_RPATHS="$(brew --prefix bzip2)" PHP_BUILD_CONFIGURE_OPTS="--with-bz2=$(brew --prefix bzip2) --with-iconv=$(brew --prefix libiconv)"  phpenv install 7.4.9

PHP_RPATHSの指定はなくても良かったかもです🙇‍♂️

\ 🎉7.4系installできたー!🎉 /

[Success]: Built 7.4.9 successfully.
$ phpenv versions
  system
  5.6.1
  7.0.31
  7.1.3
  7.1.30
  7.2.20
  7.3.0
  7.3.7
* 7.4.9 (set by /Users/xxxx/xxxx/.ruby-version)

 

そもそもconfigure optionってなに?

解決できたコマンドは以下ですが、分解して各々の環境変数が何しているか見ていきます。

$ PHP_RPATHS="$(brew --prefix bzip2)" PHP_BUILD_CONFIGURE_OPTS="--with-bz2=$(brew --prefix bzip2) --with-iconv=$(brew --prefix libiconv)"  phpenv install 7.4.9

・PHP_RPATHS

PHP_RPATHS="$(brew --prefix bzip2)" 

そもそもRPATHってなに!?

https://stackoverrun.com/ja/q/2196869 より

-rpath=dir
Add a directory to the runtime library search path. ….

訳)実行中ライブラリの検索パスにディレクトリを追加します。

\ runtime path=rpathってこと?  runtimer? /

 

wikipediaさん(https://en.wikipedia.org/wiki/Rpath)にも同じようなことが書いてある👀

configure optionにも--disable-rpathというオプションがあって、以下のような記述がされていたので、なんか解釈あってそう。

–disable-rpath

実行時にライブラリの検索パスを追加できないようにします。

 

 

・PHP_BUILD_CONFIGURE_OPTS

PHP_BUILD_CONFIGURE_OPTS="--with-bz2=$(brew --prefix bzip2) --with-iconv=$(brew --prefix libiconv)"

そのままですが、ビルド時のconfigure optionを指定できるみたいです。

--with-bz2 …bzip2を有効にし、bzip2の場所を指定をします。

--with-iconv …iconvの場所を指定できます。

 

 

参考:

https://qiita.com/kunit/items/c30147f99a48ebb159d5

https://qiita.com/hnw/items/c227d58528c147de15dd

https://www.ritolab.com/entry/211#aj_5_5

 

まとめ

PHP書いてるけど、こういうmake系は全然わかりません😭まだまだです💦

rpath=replace pathなのかと思ってた。