要約
PHPがphp.iniに記載された拡張モジュールを読み込もうとしていますが、.soファイルが存在しないか、誤った場所にあるか、異なるPHPバージョン向けにコンパイルされています。不足している拡張モジュールをインストールするか、php.iniの古いエントリをコメントアウトするか、パスを修正してください。
何が起きているのか
深夜2時にログでこのエラーを見ているはずです:
PHP Warning: PHP Startup: Unable to load dynamic library 'redis.so' (tried: /usr/lib/php/20210902/redis.so (/usr/lib/php/20210902/redis.so: cannot open shared object file: No such file or directory), /usr/lib/php/20210902/redis.so.so (/usr/lib/php/20210902/redis.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHPはphp.iniまたは/etc/php/8.1/conf.d/内のファイルからextension=redis.so(またはextension=redis)を読み込みますが、.soファイルがディスク上に存在しません。原因は主に3つあります:
- 拡張モジュールが未インストール — 誰かが
php.iniにエントリを追加したが、apt installを実行しなかった。 - PHPがアップグレードされた — 拡張モジュールのディレクトリが変わり(例:
20200930→20210902)、古い.soファイルは削除され、新しいものがまだインストールされていない。 - 拡張モジュールが手動で削除された — パッケージは削除されたが、
php.iniのエントリが残っている。
ステップ1 — 壊れた拡張モジュールを特定する
コマンドラインからPHPを実行して、起動時の警告をすべて一度に確認します:
php -v 2>&1 | head -20
# またはすべての警告を強制表示:
php -r "echo 'ok';" 2>&1
拡張モジュール名とPHPが試みたパスを正確にメモしてください。次に、ファイルが存在するか確認します:
# 'redis.so' とパスをエラーに表示されたものに置き換えてください
ls -la /usr/lib/php/20210902/redis.so
アクティブなPHP iniファイルをすべて確認します — 拡張モジュールのエントリはどこにあるかわかりません:
php --ini
# 表示内容:Loaded Configuration File + Additional .ini files
# すべてのファイルで壊れた拡張モジュール名を検索
grep -r "extension=redis" /etc/php/
修正1 — 不足している拡張モジュールをインストールする
拡張モジュールがインストールされていない場合は、ディストリビューションのパッケージマネージャーからインストールします。PHPのバージョンを必ず合わせてください:
# Ubuntu / Debian
sudo apt update
sudo apt install php8.1-redis # redisの例
sudo apt install php8.1-gd php8.1-mbstring php8.1-curl # よく使われるもの
# CentOS / RHEL (Remi リポジトリを使用)
sudo dnf install php81-php-redis
# インストール後、.so が正しい場所に配置されたか確認
ls /usr/lib/php/$(php -r 'echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION . "0" . (PHP_MAJOR_VERSION >= 8 ? "" : "");')/
PHP-FPMまたはApacheを再起動して反映させます:
sudo systemctl restart php8.1-fpm
# または
sudo systemctl restart apache2
修正2 — 古い php.ini エントリを削除する
拡張モジュールを意図的に削除していてもう必要ない場合は、ログにエラーを出し続けないよう、エントリをコメントアウトしてください:
# エントリが含まれるファイルを探す
grep -rl "extension=redis" /etc/php/8.1/
# ファイルを編集して該当行をコメントアウト
sudo nano /etc/php/8.1/mods-available/redis.ini
# 変更前: extension=redis.so
# 変更後: ;extension=redis.so
Debian/Ubuntuシステムでは、モジュールリンクを無効化する方法がより綺麗です:
sudo phpdismod redis
sudo systemctl restart php8.1-fpm
修正3 — アップグレード後のPHPバージョン不一致
これは本番環境で最もよくある原因です。PHP 8.0から8.1にアップグレードすると、拡張モジュールのディレクトリが20200930から20210902に変わります。古い.soファイルは自動的に引き継がれません。
PHPが期待するAPIバージョンを確認します:
php -i | grep 'PHP Extension'
# 出力: PHP Extension => 20210902
次に、新しいバージョン向けにすべての拡張モジュールを再インストールします:
# 旧バージョン向けにインストールされていたものをリストアップ
dpkg -l | grep php8.0
# 新しいバージョン向けに再インストール
sudo apt install php8.1-{mysql,redis,gd,mbstring,curl,xml,zip}
PECLを使って拡張モジュールを手動でコンパイルした場合は、再コンパイルが必要です:
sudo pecl uninstall redis
sudo pecl install redis
# または特定バージョンを指定
sudo pecl install redis-5.3.7
修正4 — php.ini の拡張モジュールパスが間違っている
誰かが extension ディレクティブに絶対パスをハードコードしている場合、ファイルが移動すると壊れます:
# 悪い例(ハードコードされたパス)
extension=/usr/lib/php/20200930/redis.so
# 良い例(名前だけ指定 — PHPが extension_dir を使ってパスを解決する)
extension=redis.so
# さらに短くしても可
extension=redis
PHPが実際に使用しているextension_dirを確認します:
php -i | grep extension_dir
# extension_dir => /usr/lib/php/20210902
修正の確認
# 起動時の警告が表示されないことを確認
php -r "echo 'clean';" 2>&1
# 拡張モジュールが読み込まれているか確認
php -m | grep redis
# または phpinfo で確認
php -r "phpinfo();" | grep -A2 redis
# PHP-FPM の場合はプロセスログを確認
sudo journalctl -u php8.1-fpm -n 50 --no-pager
複数のPHPバージョンやSAPI(CLI + FPM + Apache mod_php)を使用している場合、それぞれに独自のphp.iniがあります — 最初に見つけたものだけでなく、すべてを修正してください。
クイックリファレンス — よく使う拡張モジュールのパッケージ名
# Ubuntu/Debian(8.1 をお使いのバージョンに置き換えてください)
php8.1-mysql # mysqli, pdo_mysql
php8.1-pgsql # pdo_pgsql
php8.1-redis # redis
php8.1-memcached # memcached
php8.1-gd # 画像処理
php8.1-zip # zip
php8.1-xml # xml, simplexml, dom
php8.1-mbstring # マルチバイト文字列
php8.1-curl # curl
php8.1-intl # 国際化
php8.1-soap # SOAP クライアント
予防策
PHPバージョンのアップグレード前に、インストール済みの拡張モジュール一覧をダンプしておくと、後でまとめて再インストールできます:
# アップグレード前
php -m > php-extensions-before.txt
# アップグレード後、不足しているものを差分で確認
php -m > php-extensions-after.txt
diff php-extensions-before.txt php-extensions-after.txt
設定ファイルの整理として、php.iniやconf.d/のファイルをきれいに保つことは思っている以上に重要です。デプロイ設定をYAMLで管理している場合(Ansible変数、Helm valuesなど)、ToolCraftのYAML ↔ JSON Converterを使うと、サーバーに適用する前に設定ファイルの不正な記述を見つけるのに便利です — ブラウザ上で完結し、ファイルのアップロードは不要です。
また、アップグレード時に拡張モジュールが予期せず削除されないよう、デプロイスクリプトで拡張モジュールパッケージをピン留めすることも検討してください:
# プロビジョニングスクリプト / Dockerfile 内
apt-get install -y \
php${PHP_VERSION}-mysql \
php${PHP_VERSION}-redis \
php${PHP_VERSION}-gd \
php${PHP_VERSION}-mbstring

