PHP

PHPのエラーが表示されない・白い画面の調べ方|display_errorsとログ設定

PHPが白い画面になりエラーが表示されないときの切り分け方。WebとCLIの設定差、display_errors・log_errors・error_log、ini_setで構文エラーを表示できない理由を、PHP 8.3の検証例で解説します。

この記事の目次
  1. 白い画面だけで原因を決めず、PHPへ届いているかを見る
  2. php –iniの結果を、ブラウザ側の設定と同じだと思わない
  3. 開発は画面とログ、本番はログだけに出す
  4. 開発用の例
  5. 本番用の例
  6. 先頭のini_setでも、同じファイルの構文エラーには間に合わない
  7. error_logの現在値を見て、実際の書き込みまで確かめる

PHPの画面が真っ白になり、ファイルの先頭へini_setを書いても何も出ない。そんなときは、エラー表示の設定だけでなく、その設定行まで実行できているかを確認します。同じファイルに構文エラーがあると、先頭の行も実行されません。

まずWeb側で使われるPHPの設定を確かめ、開発環境ではdisplay_errors、本番ではlog_errorsとerror_logを確認するのが切り分けの順序です。error_reportingは対象にするエラー、display_errorsは画面への表示、log_errorsはログ記録を担当します。

この記事では、PHP 8.3.30の隔離したDocker環境で、警告・実行時エラー・構文エラーの表示とログを確認しています。検証した実行方式はCLIとPHPの組み込みWebサーバーです。PHP-FPMやApacheモジュールの設定方法は公式仕様を確認し、実測した範囲と分けて説明します。

白い画面だけで原因を決めず、PHPへ届いているかを見る

白く見えても、必ずPHPの致命的エラーとは限りません。空の本文を返している、リダイレクトしている、フロント側で表示できていない、PHPの前段で処理が止まっている場合もあります。

先に見るもの 次の確認先
HTTPステータスとレスポンス本文 空なのか、エラー本文はあるが画面で見えないのか
対象URLへアクセスした時刻 同じ時刻のPHP・Webサーバーのログ
問題が起きる実行方式 ブラウザ経由か、CLIのバッチか
直前に変えたファイル 構文チェックと変更箇所

HTTP 200だけでPHPのエラーを否定することもできません。今回の組み込みサーバーでは、開発用の表示設定にした致命的エラーが200で返るケースもありました。反対に、画面表示を止めた構文エラーは本文なしの500でした。ステータス・本文・ログを一緒に見ます。

WebサーバーのErrorLogやアクセスログの場所は、Apacheのログ設定で確認できます。ここからはPHP側の設定に絞ります。

スポンサーリンク

php –iniの結果を、ブラウザ側の設定と同じだと思わない

CLIとWebでは、PHPのバイナリ、実行方式、読み込む設定が異なることがあります。ターミナルでphp –iniを実行して見つかったファイルを直しても、Web側に反映されるとは限りません。

Web側で確認したいのは、PHP_VERSION、PHP_SAPI、読み込まれたini、対象設定の現在値です。次のdiagnose.phpは、必要な項目だけをJSONで表示するローカル検証用の例です。環境変数DS_LOCAL_DIAGNOSTICが1のときだけ動きます。公開サイトには置かず、実際のサーバーでは管理者だけが閲覧できる既存の診断手段を使ってください。

<?php
// 公開サイトには置かず、ローカル検証環境でだけ有効にします。
if (getenv('DS_LOCAL_DIAGNOSTIC') !== '1') {
    http_response_code(404);
    exit;
}
header('Content-Type: application/json; charset=UTF-8');
echo json_encode([
    'php_version' => PHP_VERSION,
    'sapi' => PHP_SAPI,
    'loaded_ini' => php_ini_loaded_file(),
    'scanned_ini' => php_ini_scanned_files(),
    'error_reporting' => error_reporting(),
    'display_errors' => ini_get('display_errors'),
    'display_startup_errors' => ini_get('display_startup_errors'),
    'log_errors' => ini_get('log_errors'),
    'error_log' => ini_get('error_log'),
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);

php_ini_loaded_file()は読み込まれた主な設定ファイル、php_ini_scanned_files()は追加でスキャンされたiniの一覧を返します。ただし、これだけですべての上書き元を列挙できるわけではありません。ディレクトリ設定やアプリ起動後の変更もあるため、ini_getで現在値を見ることと、設定の出所を探すことを分けます。

CLIの確認コマンドは、対象のPHP環境・コンテナ内で実行します。

php --ini
php -r 'echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;'
設定する場所 使える条件・確認点
php.iniと追加ini 対象SAPIが実際に読み込むファイルを確認する
.user.ini CGI/FastCGIで処理される。CLIやApacheモジュール向けの共通手段ではない
.htaccessのphp_flag / php_value PHPをApacheモジュールとして使い、サーバーが変更を許可している場合
ini_set() 変更可能な項目を、スクリプトが実行された時点から変える
フレームワーク・CMSのデバッグ設定 アプリ起動後に値やエラー処理が変わる場合がある

.user.iniの公式説明では、再読込間隔の既定値は300秒です。ファイル名の設定が空ならスキャンも行われません。「置いた直後に効かない」だけで記述ミスと判断しないようにします。

また、Apacheモジュールの設定変更の説明には、管理者がphp_admin_value等で固定した値はini_setなどで上書きできないとあります。どの方式でも最後にini_setが勝つ、という万能な優先順位ではありません。PHP-FPMのサイトへ、方式を確かめず.htaccessのphp_flagを書き足すことも避けます。

開発は画面とログ、本番はログだけに出す

PHPエラーの標準設定では、検知対象・画面表示・ログ記録が別々です。まず3つをそろえてから、実際に出力されることを確かめます。

設定 役割
error_reporting どの種類のエラーを対象にするか
display_errors エラーをレスポンスへ表示するか
log_errors エラーをログへ記録するか
error_log PHPエラーの記録先
html_errors 表示するエラーのHTML整形。検知や記録を有効にする設定ではない

次はphp.ini形式の設定例です。/var/log/myapp/php-error.logは例のパスなので、そのまま使う前に、公開ディレクトリの外にあり、PHP実行ユーザーが書き込める実際の場所へ置き換えます。検証時は隔離コンテナ内の書き込み可能なパスへ変更しました。

開発用の例

error_reporting = E_ALL
display_errors = On
display_startup_errors = Off
log_errors = On
error_log = /var/log/myapp/php-error.log
html_errors = Off

本番用の例

error_reporting = E_ALL
display_errors = Off
display_startup_errors = Off
log_errors = On
error_log = /var/log/myapp/php-error.log
html_errors = Off

本番ではdisplay_errorsをOffにし、利用者の画面へ内部パスやエラーの詳細を出さないようにします。その一方で、log_errorsはOnにして調査できる状態を保ちます。PHPのエラー設定の公式説明でも、画面表示とログの設定は分かれています。

display_startup_errorsは、PHP起動時のエラーを表示するための別項目です。上の例では両方ともOffにしています。起動時の問題を調べる必要がある場合だけ、外部公開していない環境で一時的に確認します。スクリプト内のini_setは、既に終わった起動処理にはさかのぼりません。

設定を変えたら、対象プロセスへの反映方法を確認してください。常駐するPHP-FPMやApacheでは管理手順に沿った再読込・再起動が必要な場合があります。反映後は、変更したファイルを読み直すだけでなく、同じWeb経路で有効値を再確認します。

先頭のini_setでも、同じファイルの構文エラーには間に合わない

構文が正しいPHPで、あとから発生する警告なら、先頭のini_setが作用します。次はローカルだけで試す警告の例です。

<?php
ini_set('display_errors', '1');
trigger_error('DS_RUNTIME_PROBE', E_USER_WARNING);

ところが、次のparse-error.phpは意図的に文字列を閉じていません。これは修正用コードではなく、設定行が動かないことを確かめる失敗例です。

<?php
ini_set('display_errors', '1');
echo 'unfinished;

このファイルは解析を通らないので、ini_setが実行されません。元の設定がdisplay_errors=Offなら、先頭へOnと書いていても表示されません。ファイルの実行前に効くini設定やログで原因を確認します。

php -l parse-error.php

php -lは構文チェックです。同じPHPバージョンで行うと、今回の閉じ忘れを実行せずに検出できます。ただし、構文が通っても未定義関数の呼び出しなど、実行時に初めて分かる問題まで検査したことにはなりません。

検証したエラー 開発用の画面 本番用の画面 ログ
E_USER_WARNINGの警告 警告を表示し、後続の出力も続く 警告は表示せず、後続の出力は続く 両方で記録
未定義関数の呼び出し エラーを表示 空のレスポンス 両方で記録
同じファイルの文字列閉じ忘れ 構文エラーを表示 ini_setが先頭にあっても空のレスポンス 両方で記録

これは独自のエラーハンドラーを付けないPHP 8.3.30の検証結果です。CMSやフレームワークがエラー画面を返す場合、その表示は別途変わります。アプリ独自のエラーハンドラーを足す前に、標準ログへ構文エラーまで届くことを確かめておくと、起動前の問題を見失いにくくなります。

error_logの現在値を見て、実際の書き込みまで確かめる

error_logにファイルパスが入っていれば、その場所を確認します。syslogならシステムのログ、未指定ならSAPIのエラーロガーへ送られます。ApacheモジュールではApacheのエラーログ、CLIでは標準エラーが例です。PHP-FPMやコンテナでは、サービス側の出力設定も確認します。

PHPから指定経路へ書けるかを試すには、ローカルの検証環境で、固有の短い文字列を1回送ります。

<?php
error_log('DS_LOG_ROUTE_PROBE');
echo 'Log probe sent; check the destination file.';

画面の「送信した」という文だけで成功と判定せず、実際のログにDS_LOG_ROUTE_PROBEがあるかを探してください。これは明示的なerror_log()呼び出しの経路確認であり、自動エラー記録の全設定を検証するものではありません。log_errorsとerror_reportingは、警告などの再現でも確かめます。

見つからなければ、対象リクエストの設定、パス、親ディレクトリの存在、実行ユーザーの書き込み権限、コンテナ内外のパスを順に確認します。読み込んだ設定値が正しくても、そのファイルへ書けるとは限りません。

「表示をOnにしたのに白い」で止まったら、Webの有効値、構文チェック、出力先の実記録へ戻ってください。開発で原因を直したあとは、本番の画面表示をOffに保ちながら、必要なログが残る状態を確認して調査を終えます。

スポンサーリンク