エラー処理 | Symfony7
対象: Symfony 7.4(
symfony/runtime、symfony/error-handler、FrameworkBundle のソースで確認)
版によって挙動が変わる可能性があります。vendor/の実コードで確認してください。
全体像
PHP のエラー(Warning・Notice など)の扱いは、2 段階で決まります。
| 段階 | タイミング | 担当 | 内容 |
|---|---|---|---|
| 1 | アプリ起動直後 | Symfony\Component\Runtime\Internal\SymfonyErrorHandler::register() |
error_reporting・display_errors・assertions・DebugClassLoader の設定、ErrorHandler の登録 |
| 2 | カーネル boot 時 | FrameworkBundle(debug.error_handler_configurator) |
framework.php_errors の log / throw を反映して、例外化・ログ出力の対象を確定 |
段階 2 の throw の既定値が %kernel.debug% であることが、APP_DEBUG による最大の差異です(後述)。
段階 1: SymfonyErrorHandler
// vendor/symfony/runtime/Internal/SymfonyErrorHandler.php(抜粋)
public static function register(bool $debug): void
{
// ...
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED);
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
ini_set('display_errors', $debug);
} elseif (!filter_var(\ini_get('log_errors'), \FILTER_VALIDATE_BOOL) || \ini_get('error_log')) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}
if (0 <= \ini_get('zend.assertions')) {
ini_set('zend.assertions', (int) $debug);
}
ini_set('assert.active', 1);
ini_set('assert.exception', 1);
if ($debug) {
DebugClassLoader::enable();
}
ErrorHandler::register(new ErrorHandler(new BufferingLogger(), $debug));
}
error_reporting
error_reporting(\E_ALL & ~\E_DEPRECATED & ~\E_USER_DEPRECATED) は APP_DEBUG に関係なく設定されます。
| 式 | 意味 |
|---|---|
\E_ALL |
全エラーレベルを対象にする |
~\E_DEPRECATED |
E_DEPRECATED(PHP 本体の非推奨警告)を除外 |
~\E_USER_DEPRECATED |
E_USER_DEPRECATED(trigger_error による非推奨警告)を除外 |
つまり「Deprecated 系以外を PHP のエラー報告対象にする」設定です。
[!NOTE]
error_reporting()は、PHP 自身が表示・ログするエラーの範囲を決める設定です。カスタムエラーハンドラの呼び出しは抑止しません(PHP マニュアルに明記)。
Symfony のErrorHandlerはset_error_handler()の第 2 引数(mask)に「例外化する種別 | ログする種別」を渡して登録します。Deprecated 系もこのハンドラに届くため、PHP 側の表示から外しても、Symfony 側でログ出力(info)できます。
display_errors
| SAPI | 挙動 |
|---|---|
Web(cli / phpdbg / embed 以外) |
ini_set('display_errors', $debug)(APP_DEBUG=true なら 1、false なら 0) |
| CLI | log_errors が無効、または error_log が設定されている場合のみ display_errors=1。log_errors 有効かつ error_log 未設定なら変更しない(PHP の設定のまま) |
CLI の分岐は $debug を参照しないため、APP_DEBUG による差異はありません。ただし「常に 1」ではない点に注意してください。
DebugClassLoader
APP_DEBUG=true のときだけ有効になります。クラスのロード時に、開発者向けの次のような問題を検出して非推奨警告を出します。
- クラス名の大文字・小文字の不一致(ファイル名との差異)
@deprecatedなクラス・メソッドの利用、@finalの違反- 戻り値型の宣言漏れなど、将来の BC break につながる書き方
本番では不要なため無効です。エラーの例外化には影響しません。
段階 2: FrameworkBundle による例外化の設定
framework.php_errors の既定値は次のとおりです。
| 設定 | 既定値 | 意味 |
|---|---|---|
throw |
%kernel.debug% |
PHP エラーを \ErrorException として投げる |
log |
true |
PHP エラーをアプリのロガー(Monolog)へ出力する |
throw が false の場合、FrameworkBundle は throwAt(0) 相当の設定を行います。このとき例外化されるのは、常に例外化される E_RECOVERABLE_ERROR と E_USER_ERROR だけです。
APP_DEBUG による差異(カーネル boot 後)
| 項目 | APP_DEBUG=true |
APP_DEBUG=false |
|---|---|---|
| Warning / Notice の例外化 | される | されない(ログ出力のみ) |
E_RECOVERABLE_ERROR / E_USER_ERROR |
例外化 | 例外化(同じ) |
| Deprecated 系 | 例外化されない(ログのみ) | 例外化されない(ログのみ) |
display_errors(Web) |
1 | 0 |
display_errors(CLI) |
前述の条件による(同じ) | 前述の条件による(同じ) |
DebugClassLoader |
有効 | 無効 |
zend.assertions(既定が 0 以上のとき) |
1 | 0 |
[!WARNING]
ErrorHandlerのprivate int $thrownErrors = 0x1FFF;はプロパティの初期値です。カーネル boot 前(.envの読み込みやカーネル生成の最中)はこの値が有効で、Warning も例外化されます。boot 後はphp_errors.throwが反映されます。
本番でもローカルと同じ挙動にしたい場合
Warning・Notice を本番でも例外化するには、明示的に設定します。
# config/packages/framework.yaml
framework:
php_errors:
throw: true
逆に、ローカルでも本番と同じ「ログのみ」にしたい場合は throw: false を when@dev などに設定します。
例外化される対象
- Warning・Notice などの非致命エラーは
\ErrorExceptionになります(ErrorHandler::handleError())。 - PHP 7 以降の
\Error(TypeError、DivisionByZeroErrorなど)は最初から例外であり、\Errorのまま投げられます。ErrorExceptionにはなりません。 - 回復不能な Fatal(
E_ERRORなど)は shutdown 時に検出され、FatalErrorとして扱われます。 @演算子で抑制されたエラーは、(E_RECOVERABLE_ERRORなどを除き)例外化されません。
確認方法
# 設定の確認(throw / log の実際の値)
bin/console debug:config framework php_errors
# 実挙動の確認: Warning を出すコマンドを作成して、環境ごとに実行する
# trigger_error('sample', E_USER_WARNING);
APP_ENV=dev APP_DEBUG=1 bin/console app:sample # 例外になる
APP_ENV=prod APP_DEBUG=0 bin/console app:sample # ログのみ(error レベル)
まとめ
error_reportingと Deprecated 系の除外は、APP_DEBUGに関係なく共通です。- Warning・Notice の例外化は
APP_DEBUGによって変わります(framework.php_errors.throwの既定が%kernel.debug%)。APP_DEBUG=falseではログのみです。 - CLI の
display_errorsはAPP_DEBUGに依存せず、log_errors/error_logの設定で決まります。 - 環境間の挙動を揃えるには、
framework.php_errors.throwを明示するのが確実です。 - ログレベルの対応は Monolog のエラーハンドリング を参照してください。