WP-CLI が全部落ちた。原因は自分のプラグインが読み込んでいた Composer だった

PHP のバージョン不足で Composer の platform_check.php が Fatal error を投げ、WordPress サイトが落ちる様子を表したアイキャッチ WordPress
この記事は約11分で読めます。

2026年8月10日。Xserver に SSH で入って、サイトの棚卸し用に書いたスクリプトを wp eval-file で回そうとしたら、結果ではなく Fatal error が返ってきました。別のコマンドを試しても同じ。wp core version でさえ落ちる。WP-CLI が丸ごと死んでいます。

サイトのほうは何事もなく表示されていました。管理画面も普通に開く。壊れているのはコマンドラインだけです。しばらく WP-CLI 側を疑って、phar を入れ直そうとしたり、パスを確認したりしていました。原因が自分の書いたプラグインだと分かるまでに、しばらくかかっています。

検証したのは Xserver スタンダードプラン、SSH 接続、WordPress 7.0 系のサイトです。Web 側の PHP は 8.2 以上に設定してあり、SSH の php -v が返したのは 8.0.30 でした。

同じサーバーの同じプラグインでも、Web からは PHP 8.2 以上、SSH からは 8.0.30 が使われ、CLI だけが落ちることを示した図

修正前は vendor/autoload.php を読み込んで platform_check.php が Fatal error を投げ、修正後は PHP_VERSION_ID の確認で return して autoload に到達しないことを比較した図

上の2枚が、この記事で起きたことの全体です。1枚目が、同じサーバーの同じプラグインなのに Web と CLI で結果が分かれた構図。2枚目が、直したあとにどこで止まるようになったか。以下は、この2枚にたどり着くまでの順番です。

WP-CLI が落ちていたのは、自分のプラグインが原因でした

エラーの本文を読み直したら、そこに自分のプラグインのパスが入っていました。サーバー名とユーザー名だけ伏せて、そのまま貼ります。

スタックトレースは長いので途中を省いています。読みたいのは #4 と #0 の2行です。#4 が rapls-passkey-pro.php の61行目、つまり自分が書いた require。そこから2つ下って、#0 で Composer の platform_check.php が例外を投げています。最後の Error: このサイトで重大なエラーが発生しました。 は WordPress が出したもので、致命的エラーとして扱われたことを示しています。

途中に scoper-autoload.php が挟まっているのは、依存ライブラリの名前空間を php-scoper で書き換えているからです。自分のプラグインが読んでいるのは直接 vendor/autoload.php ではなく、その手前のファイル。ただ、どちらを読んでも最終的に platform_check.php に到達するので、今回の話には影響しません。

Composer で作った vendor/ の中には composer/platform_check.php というファイルが入っていて、vendor/autoload.php を読み込んだ瞬間に走ります。composer.json が要求している PHP のバージョンを、実行中の PHP が満たしているかを確認するだけの短いファイルです。満たしていなければ、そこで例外を投げて止まる。

止まるのは正しい動きです。依存ライブラリが 8.2 の書き方を使っているのに 8.0 で走らせれば、どのみちどこかで壊れる。手前で止めるほうが親切だという設計思想は分かります。問題は、止まり方のほうでした。

Xserver は Web と SSH で PHP のバージョンが違います

ここで効いていたのが、Xserver の仕様です。サーバーパネルで設定する PHP のバージョンは、Web からアクセスされたときに使われるものです。SSH でログインして php と打ったときに動くのは、それとは別の、サーバー全体のデフォルトになります。

知識としては知っていました。過去に .bashrc でパスを通した記憶もあります。ただ、この件で長く迷ったのは、CLI の PHP が 8.0.30 という中途半端に新しいバージョンだったからです。よく見かける事例は 5.4.16 が返ってくるパターンで、そちらなら見た瞬間に古いと分かる。8.0 は、言われなければ気づきません。動くコマンドも多い。

結果として、Web では動いてサイトは無事なのに、CLI だけが落ちるという状態になっていました。同じサーバーの、同じプラグインです。読み込む PHP が違うだけで、片方は無事、片方は全滅する。

自分のコードは、バージョンを確認せずに require していました

スタックトレースの #4 が指していた、プラグイン本体の 61 行目を見に行きました。vendor/autoload.php を、何の確認もせずに require しています。書いた当時の私は、Composer の依存を読み込むのだから当然そう書くものだと思っていたはずです。

ここに問題があると分かってから、しばらく考え込みました。プラグインが動かないこと自体は、私の想定内です。PHP 8.2 を要求すると決めたのは自分だし、満たさない環境で機能しないのは仕方がない。困るのは、動かないことではなく、動かないときにサイトごと道連れにすることのほうでした。

WordPress.org のサポートフォーラムを検索すると、同じ形の報告がいくつも出てきます。プラグインを更新した瞬間に管理画面ごと真っ白になり、リカバリーモードにも入れず、FTP でフォルダをリネームするしかなくなる。開発者が「これはこちらのミスでした。バージョンを確認して警告を出す形に直します」と返している例もありました。私が今いるのは、その返信を書く側の位置です。

直したのは、読み込む前に確認するという一点だけです

やったことは単純で、vendor/autoload.php を require する手前に、PHP のバージョンを見る分岐を置きました。足りなければ、そこで return して以降を一切実行しません。

PHP_VERSION_ID で早期に return する

PHP_VERSION_ID は 8.0.30 なら 80030 という整数を返す定数です。文字列比較より速く、書き間違いも起きにくい。8.2 以上かどうかを見るなら 80200 と比べます。この判定は Composer の autoload より前に走らせる必要があるので、プラグインファイルのほぼ先頭に置きました。

管理画面には admin_notices で伝える

黙って止まると、利用者は「入れたのに何も起きない」という状態に置かれます。それは白画面とは別の意味で不親切なので、管理画面に警告を出すようにしました。動いていないこと、必要な PHP のバージョン、いま動いている PHP のバージョン。この3つが並んでいれば、次に何をすればいいかは自分で分かります。

WP-CLI では warning を返す

コマンドラインには admin_notices が届きません。WP_CLI クラスが定義されているかを見て、あれば WP_CLI::warning() を呼ぶようにしました。修正後に Xserver で叩き直したら、Fatal error ではなくこう返ってきます。

Warning: Rapls Passkey Pro is not running: PHP 8.2 or later is required, and this is PHP 8.0.30.

そして、その後ろに wp plugin list の結果がちゃんと出る。他のプラグインの作業が止まらなくなりました。

プラグインヘッダに Requires PHP を書く

ヘッダに Requires PHP: 8.2 を書いておくと、WordPress 本体が有効化の時点で止めてくれます。管理画面から入れる限り、そもそも 8.2 未満の環境では有効化できなくなる。コードのガードとは別の層で効くので、両方書いておくのが正しい形でした。

ただ、これだけでは足りません。ヘッダが効くのは有効化のときだけです。有効化したあとにサーバーの PHP を下げた場合や、今回のように Web と CLI でバージョンが違う場合は、素通りします。実際、今回それで通り抜けられました。

そもそも 8.2 を要求していたのは誰なのか

直したあとで、要求バージョンの出所を追いました。composer.json に自分で 8.2 と書いた覚えがなかったからです。

たどっていくと、パスキーの実装に使っている web-auth/webauthn-lib の v5 が PHP 8.2 以上を要求していました。v4 なら 8.1 でも動くようです。依存ライブラリのメジャーバージョンを1つ下げれば、要求を 8.1 に緩められる余地があることになります。

下げるかどうかは、まだ決めていません。8.1 はすでにセキュリティ修正も終わっているバージョンなので、そこに合わせに行くのが利用者のためになるとも言い切れない。ただ、自分のプラグインが要求している数字が、自分で決めたものではなく依存から来ていたという事実は、知らないままにしておくべきではなかったと思います。

余談ですが、この日いちばん時間を使ったのは WP-CLI の phar を入れ直そうとしていた30分でした。壊れているのは自分の側だと思わないうちは、まわりの道具ばかり疑ってしまう。

次の自分に渡すメモ

Composer を同梱したプラグインを書くときは、vendor/autoload.php の require より前にバージョンを見る。足りなければ return して、管理画面には admin_notices、CLI には WP_CLI::warning。ヘッダの Requires PHP も書くが、あれは有効化のときにしか効かないので、コード側のガードと両方いる。

Xserver で WP-CLI が落ちたら、まず php -v を見る。サーバーパネルの設定は Web 側の話で、SSH は別物。5.4 が返ってくる事例ばかり見ていると、8.0 が返ってきたときに見逃す。

自分のプラグインが要求している PHP のバージョンは、依存ライブラリから来ていることがある。composer.json に書いた数字だけを見て決めた気になっていると、どこで決まったのか分からなくなる。

まだ確かめていないのは、8.1 まで下げる余地を実際に取るかどうかです。web-auth/webauthn-lib を v4 に落としたときに、いま使っている API がそのまま通るのかを見ていません。

入れたコード

プラグイン本体の先頭、vendor/autoload.php を require する手前に置いています。

プラグインファイルのトップレベルで return しているので、この下に書いた処理は一切走りません。vendor/autoload.php に到達しないということは、platform_check.php も走らない。例外が投げられる場所まで行かせないのが、この形の目的です。

関連記事

WordPress
この記事を書いた人
rapls

WordPressのプラグインを作っているフリーランスエンジニアです。Web開発はもう6年以上。WordPress.orgで Rapls AI Chatbot、Thanks Mail for Stripe、Rapls PDF Image Creator、Prime Cache の4本を公開し、保守を続けながら、日本語ロケールの翻訳エディター(PTE)も務めています。このブログに書くのは、現場で自分が実際にハマって、調べて、直した話です。

raplsをフォローする
raplsをフォローする

コメント

タイトルとURLをコピーしました