スマホ表示だけレイアウトが崩れ、「重大なエラー」が出た話

ある日、サイトを確認していて気づきました。

PC表示は問題ありません。表示速度もレイアウトも正常です。 ところがスマホで表示してみると、

  • 一瞬だけ正常に表示される
  • 直後にレイアウトが崩れる
  • ページ下部に「このサイトで重大なエラーが発生しました。」と表示される

という、非常に嫌な挙動が発生していました。

 
現在は解決しましたので、原因の特定方法から備忘として記事にしてみたいと思います。

まず疑ったこと(しかし違いました)

最初に疑ったのは、よくある原因です。

  • CSSの崩れ
  • JavaScriptの競合
  • スマホ用スタイルの記述ミス
  • キャッシュや最適化プラグイン

初めはその線でプラグインを全て無効化してみたり、ブラウザからソースを確認したりしてみましたが、調べた結果実際には、

  • キャッシュ系プラグインは使用していなかった
  • PC表示は常に安定している
  • 「崩れる」というより「処理が途中で落ちている」挙動

これらの点から、CSSやJSではなく、PHP側でエラーが発生している可能性が高いと判断しました。

決定打はサーバーのエラーログ

ブラウザ画面には具体的なエラー内容が表示されませんでした。 このような場合、サーバー側のPHPエラーログを確認する必要があります

利用しているレンタルサーバーからからエラーログを確認し、 スマホ表示でエラーを再現した直後にログを見たところ、

PHP Fatal error: Call to undefined function is_bot()

というエラーが記録されていました。

原因:未定義関数 is_bot()

エラー内容は非常にシンプルでした。

Call to undefined function is_bot()

みたままですが、存在しない関数を呼び出しているということです。

該当箇所は、使用しているテーマ「THE THOR」の single.php でした。

調べてみると、

  • スマホ表示時
  • ユーザーエージェント判定などの特定条件

でのみ is_bot() という関数を呼び出す処理があり、 その関数自体がどこにも定義されていない状態でした。

PC表示ではこの分岐を通らないため、 スマホ表示時だけエラーが発生していた、というわけです。

なぜ今まで問題にならなかったのか

考えられる理由は以下の通りです。

  • PHP7系では表面化しにくかった
  • 特定条件でしか実行されないコードだった
  • PHP8系で未定義関数が即Fatal Error扱いになった

いつからエラーが出ていたのか定かではありませんが、PHPのバージョンアップか、テーマのアップデートか、何らかの環境変化によって、 テーマ内部に潜んでいた不整合が表に出たケースだと思います。

対応:子テーマで is_bot() を定義

テーマ本体を直接修正するのは避けたいところです。理由は単純で、アップデート時に修正内容が上書きされてしまうためです。

そのため今回は、子テーマ側で、本体で未定義の関数を補完するという方法を取りました。

手順① 子テーマの functions.php を開く

まず、子テーマの functions.php を開きます。wordpress管理画面にログインできればそこから、もしできないなら

  • FTP
  • サーバーのファイルマネージャー

等で、以下のファイルを編集します。

/wp-content/themes/(子テーマ名)/functions.php

手順② is_bot() が存在しない場合のみ定義するコードを追記

次に、ファイルの末尾に以下のコードを追加しました。

if ( ! function_exists('is_bot') ) {
    function is_bot() {
        if ( empty($_SERVER['HTTP_USER_AGENT']) ) {
            return false;
        }

        $bots = [
            'Googlebot',
            'bingbot',
            'Slurp',
            'DuckDuckBot',
            'Baiduspider',
            'YandexBot',
            'Sogou',
            'Exabot',
            'facebot',
            'ia_archiver'
        ];

        foreach ( $bots as $bot ) {
            if ( stripos($_SERVER['HTTP_USER_AGENT'], $bot) !== false ) {
                return true;
            }
        }

        return false;
    }
}

ポイントは、function_exists() を使っている点です。これにより、

  • 将来テーマ側で is_bot() が定義されても競合しない
  • 二重定義によるエラーを防げる

というメリットがあります。

手順③ 保存後、スマホ表示で動作確認

保存後、スマホ表示で該当の記事ページを確認しました。

結果は問題なし。あわせて、サーバーの error_log を確認し、 新しい Fatal Error が出ていないこともチェックしました。

この対応により、テーマ側が本来想定していた処理を壊すことなく、 安全に問題を解消することができました。

結果:スマホ表示も完全に正常化しました

対応後は、

  • スマホ表示でのレイアウト崩れなし
  • 「重大なエラー」表示も出ない
  • PHPエラーログにも新しいエラーは出ない

という状態になり、問題は完全に解消しました。

「スマホ表示だけおかしい」、「一瞬表示されてから崩れる」、このような症状が出た場合、 CSSやJavaScriptを疑う前に、PHPのFatal Errorを確認することが非常に重要だと実感しました。

まとめ

今回の不具合は、

  • 表示が一瞬正常に見える
  • スマホ表示のみで発生する
  • 毎回必ず起きるように見えない(PCでは正常に処理)

といった条件が重なり、原因特定が難しいケースでした。

しかし実際には、未定義の関数が呼び出される以上、条件に入れば必ず Fatal Error になる問題であり、
「不定期に見えた」のは、テーマ内の分岐条件による錯覚でした。テーマ側の実装に起因する不具合であっても、

  1. ログを確認する
  2. エラーの正体を把握する
  3. 子テーマで安全に補完する

という手順を踏めば、テーマ本体を壊さずに現実的な解決策を取ることができます

すべてをテーマ作者任せにせず、「自分のサイトとしてどう安全に運用するか」を考えることも、WordPress を長く使っていく上では大切だと感じたトラブルでした。

 
同様の不具合に悩んでいる人の参考になれば幸いです。