PHP 8.3 で set_error_handler が deprecation を取りこぼす条件と実務での回避

这篇内容暂时只有 日本語 版本,以下是原文。

PHP 8.3.4 / Ubuntu 22.04 / nginx + PHP-FPM の構成で、社内の集計バッチを動かしていたときに気づいた話です。データ量は1日あたりの処理行数が数十万、1プロセスあたりのメモリ上限は128M。PHP 8.3 に上げた直後から、ログに Deprecated: ... が出ているのにアプリ側のエラーハンドラが拾わず、監視に何も上がらない状態が数日続きました。

原因は set_error_handler のコールバックで error_reporting() を評価していなかったことでした。8.3 では deprecation の種類が増えており、E_ALL を前提にしたつもりでも、実行時の error_reporting が E_ALL & ~E_DEPRECATED に落ちている箇所がありました。ハンドラは呼ばれるが自前で握りつぶしていた、というより、ハンドラ内で level を見て return false していたのが実際の挙動です。

set_error_handler(function (int $no, string $str, string $file, int $line): bool {
    if (!(error_reporting() & $no)) {
        return false; // ここで握ると監視に届かない
    }
    throw new ErrorException($str, 0, $no, $file, $line);
});

この形にしてから deprecation が ErrorException として上がるようになり、バッチ側で catch して集計できるようになりました。代償は明確で、サードパーティの古いコードが吐く deprecation まで例外化されるため、@ 演算子や error_reporting(0) に依存した処理が軒並み落ちます。うちではベンダーコードの呼び出し区間だけ一時的に error_reporting(E_ALL & ~E_DEPRECATED) に落とすラッパーを挟みました。

効かないのは、FPM の php.ini で display_errors=Off かつ log_errors=On のとき、ハンドラをすり抜けたエラーがファイルにだけ出るケースです。あと @ 付きの式は 8.0 以降でも error_reporting() が一時的に 0 になるので、上のコードだと握れません。ここは @ を外すのが本筋ですが、外部SDKだと難しいことがあります。

同じように 8.3 へ上げて deprecation が監視に乗らない現象、どの層(FPM設定・ハンドラ・フレームワーク)で止めていますか。

0
赞同度 100% · 0 条评论 · 6 阅读

0 条评论

支持 Markdown · 回复会实时推送给对方

还没有评论,写下第一条。