web-dev-qa-db-ja.com

register_shutdown_function()を使用してPHPで致命的なエラーを処理します

この回答のコメント によると、set_error_handler()を使用してキャッチできない シャットダウン関数 を介して致命的なエラーをキャッチできます。

ただし、致命的なエラーが原因でシャットダウンが発生したのか、スクリプトが終了したのかを判断する方法を見つけることができませんでした。

さらに、デバッグバックトレース関数はシャットダウン関数では機能しないため、致命的なエラーが発生したスタックトレースをログに記録することはほとんど意味がありません。

私の質問は、適切なバックトレースを作成する能力を維持しながら、致命的エラー(特に未定義の関数呼び出し)に対応する最良の方法は何ですか?

36
ThiefMaster

これは私のために働く:

function shutdown() {
    $error = error_get_last();
    if ($error['type'] === E_ERROR) {
        // fatal error has occured
    }
}

register_shutdown_function('shutdown');

spl_autoload_register('foo'); 
// throws a LogicException which is not caught, so triggers a E_ERROR

ただし、おそらく既に知っていますが、念のため:E_ERRORから回復することはできません。

バックトレースについては、できません... :(致命的なエラーのほとんどの場合、特にUndefined functionエラーでは、本当に必要ありません。ファイルを特定することはできません。/lineが発生した場所で十分です。その場合、バックトレースは無関係です。

65
netcoder

Register_shutdown_functionを使用して致命的なエラーと適切なアプリケーションのシャットダウンを区別する1つの方法は、定数をプログラムの最終行として定義し、その定数が定義されているかどうかを確認することです。

function fatal_error() {
if ( ! defined(PROGRAM_EXECUTION_SUCCESSFUL)) {
        // fatal error has occurred
    }
}

register_shutdown_function('fatal_error');

define('PROGRAM_EXECUTION_SUCCESSFUL', true);

プログラムが最後に到達した場合、致命的なエラーが発生することはないため、定数が定義されている場合は関数を実行しないようにします。

error_get_last()は、前述のようにバックトレースはありませんが、デバッグする必要がある致命的なエラーに関するすべての情報を含む配列です。

一般的に、PHPプログラムで(例外ではなく)致命的なエラーが発生した場合、問題を見つけて修正できるようにプログラムを爆破する必要があります。 register_shutdown_functionは、エラー報告をオフにしたいが、それに対応できるようにエラーをバックグラウンドでログに記録する方法が必要な実稼働環境に役立つことがわかりました。このようなエラーが発生した場合、関数を使用してユーザーにわかりやすいhtmlページを表示させることもできます。これにより、空白のページを表示するだけでは済みません。

10
Gabriel

現在のerror_handlerメソッドを取得するための素敵なトリック=)

<?php
register_shutdown_function('__fatalHandler');
function __fatalHandler()
{
    $error      = error_get_last();

    //check if it's a core/fatal error, otherwise it's a normal shutdown
    if($error !== NULL && $error['type'] === E_ERROR) {
        //Bit hackish, but the set_exception_handler will return the old handler
        function fakeHandler() { }
        $handler = set_exception_handler('fakeHandler');
        restore_exception_handler();
        if($handler !== null) { 
            call_user_func($handler, new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
        }
        exit;
    }
}
?>

また、私はあなたが電話すれば

<?php
ini_set('display_errors', false);
?>

PHPはエラーの表示を停止します。そうしないと、エラーハンドラーの前にエラーテキストがクライアントに送信されます

6
Sander Visser