意図 - PHPの致命的なエラーをどのように捕捉するのですか?




xampp fatal error (12)

Zend Framework 2の優れたソリューション:

/**
 * ErrorHandler that can be used to catch internal PHP errors
 * and convert to an ErrorException instance.
 */
abstract class ErrorHandler
{
    /**
     * Active stack
     *
     * @var array
     */
    protected static $stack = array();

    /**
     * Check if this error handler is active
     *
     * @return bool
     */
    public static function started()
    {
        return (bool) static::getNestedLevel();
    }

    /**
     * Get the current nested level
     *
     * @return int
     */
    public static function getNestedLevel()
    {
        return count(static::$stack);
    }

    /**
     * Starting the error handler
     *
     * @param int $errorLevel
     */
    public static function start($errorLevel = \E_WARNING)
    {
        if (!static::$stack) {
            set_error_handler(array(get_called_class(), 'addError'), $errorLevel);
        }

        static::$stack[] = null;
    }

    /**
     * Stopping the error handler
     *
     * @param  bool $throw Throw the ErrorException if any
     * @return null|ErrorException
     * @throws ErrorException If an error has been catched and $throw is true
     */
    public static function stop($throw = false)
    {
        $errorException = null;

        if (static::$stack) {
            $errorException = array_pop(static::$stack);

            if (!static::$stack) {
                restore_error_handler();
            }

            if ($errorException && $throw) {
                throw $errorException;
            }
        }

        return $errorException;
    }

    /**
     * Stop all active handler
     *
     * @return void
     */
    public static function clean()
    {
        if (static::$stack) {
            restore_error_handler();
        }

        static::$stack = array();
    }

    /**
     * Add an error to the stack
     *
     * @param int    $errno
     * @param string $errstr
     * @param string $errfile
     * @param int    $errline
     * @return void
     */
    public static function addError($errno, $errstr = '', $errfile = '', $errline = 0)
    {
        $stack = & static::$stack[count(static::$stack) - 1];
        $stack = new ErrorException($errstr, 0, $errno, $errfile, $errline, $stack);
    }
}

このクラスを使用すると、必要に応じて特定のErrorHandlerを開始することができます。 その後、ハンドラを停止することもできます。

たとえば、次のようにこのクラスを使用します。

ErrorHandler::start(E_WARNING);
$return = call_function_raises_E_WARNING();

if ($innerException = ErrorHandler::stop()) {
    throw new Exception('Special Exception Text', 0, $innerException);
}

// or
ErrorHandler::stop(true); // directly throws an Exception;

完全なクラスコードへのリンク:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


おそらくもっと良い解決策はMonologです:

完全なクラスコードへのリンク:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

また、 register_shutdown_function関数を使用してFATAL_ERRORSを処理することもできます。 このクラスによると、FATAL_ERRORは次のarray(E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR)です。

class ErrorHandler
{
    // [...]

    public function registerExceptionHandler($level = null, $callPrevious = true)
    {
        $prev = set_exception_handler(array($this, 'handleException'));
        $this->uncaughtExceptionLevel = $level;
        if ($callPrevious && $prev) {
            $this->previousExceptionHandler = $prev;
        }
    }

    public function registerErrorHandler(array $levelMap = array(), $callPrevious = true, $errorTypes = -1)
    {
        $prev = set_error_handler(array($this, 'handleError'), $errorTypes);
        $this->errorLevelMap = array_replace($this->defaultErrorLevelMap(), $levelMap);
        if ($callPrevious) {
            $this->previousErrorHandler = $prev ?: true;
        }
    }

    public function registerFatalHandler($level = null, $reservedMemorySize = 20)
    {
        register_shutdown_function(array($this, 'handleFatalError'));

        $this->reservedMemory = str_repeat(' ', 1024 * $reservedMemorySize);
        $this->fatalLevel = $level;
    }

    // [...]
}

私はset_error_handler()を使用してほとんどのPHPエラーをキャッチできますが、存在しない関数を呼び出すなどの致命的なエラー( E_ERROR )では機能しません。 これらのエラーをキャッチする別の方法はありますか?

私はすべてのエラーのためにmail()を呼び出そうとしており、PHP 5.2.3を実行しています。


PHP> = 5.1.0を使用している場合は、ErrorExceptionクラスを使用して次のようにします。

<?php
//define an error handler
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
//set ur error handle
set_error_handler("exception_error_handler");

/* Trigger exception */
try
{
  //try to do something like finding the end of the internet
}
catch(ErrorException $e)
{
  //anything you want to do with $e
}

?>

PHPは、致命的なエラーをキャッチして回復する従来の手段を提供していません。 これは、処理が通常は致命的なエラーの後に回復されるべきではないためです。 出力バッファにマッチする文字列(PHP.netで説明されている手法の元の投稿で示唆されているように)は間違いのようです。 それは単に信頼できません。

エラーハンドラメソッド内からmail()関数を呼び出すことも問題であることが判明しています。 エラーが多かった場合、メールサーバーに作業がロードされ、ぎこちない受信トレイで自分自身を見つけることができます。 これを避けるには、cronを実行してエラーログを定期的にスキャンし、それに応じて通知を送信することを検討してください。 また、 Nagiosなどのシステム監視ソフトウェアを調べることもできます。

シャットダウン機能の登録に関する話をするには:

シャットダウン機能を登録することは事実ですが、それは良い答えです。

ここでのポイントは、通常、出力バッファに対して正規表現を使用することではなく、致命的なエラーから回復しようとするべきではないということです。 私は受け入れられた答えに応答していました。これはその後変更または削除されたphp.netに関する提案にリンクしています。

その提案は、例外処理中に出力バッファに対して正規表現を使用することでした。致命的なエラー(設定されたエラーテキストと一致することによって検出されたもの)で、何らかのリカバリや処理を続行してください。 それは推奨される方法ではありません(元の提案を見つけることができないと私は思っています。私はそれを見落としているか、PHPコミュニティがそれを撃っています)。

出力バッファリングコールバックが呼び出される前に、より新しいバージョンのPHP(約5.1)がシャットダウン関数を早期に呼び出すように見えることは注目に値するかもしれません。 バージョン5以前では、その順序は逆でした(出力バッファリングコールバックの後にシャットダウン機能が続きました)。 また、約5.0.5(質問者のバージョン5.2.3よりはるかに早い)から、登録されたシャットダウン関数が呼び出される前にオブジェクトがアンロードされているので、メモリ内のオブジェクトに依存することはできません多くのもの

したがって、シャットダウン機能の登録はうまくいきますが、シャットダウン機能によって実行されるべき種類のタスクは、ほんの一握りの穏やかなシャットダウン手順に限られています。

ここで鍵となるのは、この質問に遭遇し、最初に受け入れられた答えの助言を見る人のための知恵の言葉です。 出力バッファを正規表現しないでください。


register_shutdown_functionを使用して致命的なエラーをregister_shutdown_functionregister_shutdown_function 。これにはPHP 5.2以降が必要です:

register_shutdown_function( "fatal_handler" );

function fatal_handler() {
    $errfile = "unknown file";
    $errstr  = "shutdown";
    $errno   = E_CORE_ERROR;
    $errline = 0;

    $error = error_get_last();

    if( $error !== NULL) {
        $errno   = $error["type"];
        $errfile = $error["file"];
        $errline = $error["line"];
        $errstr  = $error["message"];

        error_mail(format_error( $errno, $errstr, $errfile, $errline));
    }
}

error_mailformat_error関数を定義する必要があります。 例えば:

function format_error( $errno, $errstr, $errfile, $errline ) {
    $trace = print_r( debug_backtrace( false ), true );

    $content = "
    <table>
        <thead><th>Item</th><th>Description</th></thead>
        <tbody>
            <tr>
                <th>Error</th>
                <td><pre>$errstr</pre></td>
            </tr>
            <tr>
                <th>Errno</th>
                <td><pre>$errno</pre></td>
            </tr>
            <tr>
                <th>File</th>
                <td>$errfile</td>
            </tr>
            <tr>
                <th>Line</th>
                <td>$errline</td>
            </tr>
            <tr>
                <th>Trace</th>
                <td><pre>$trace</pre></td>
            </tr>
        </tbody>
    </table>";
    return $content;
}

Swift Mailerを使用してerror_mail機能を作成します。

参照:


あなたは致命的なエラーを捕まえることはできませんが、それらを記録/報告することはできます。 簡単なデバッグのために、私はこの簡単なコードに1つの答えを修正しました

function __fatalHandler()
{
    $error = error_get_last();
//check if it's a core/fatal error, otherwise it's a normal shutdown
    if ($error !== NULL && in_array($error['type'], array(E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING,E_RECOVERABLE_ERROR))) {
        echo "<pre>fatal error:\n";
        print_r($error);
        echo "</pre>";
        die;
    }
}

register_shutdown_function('__fatalHandler');

あんまり。 彼らが致命的であるため、致命的なエラーはそれと呼ばれます。 あなたはそれらから回復することはできません。


この関数を開発して、致命的なエラーを引き起こす可能性のあるコードを「サンドボックス」することを可能にしました。 クロージャーregister_shutdown_functionからスローされた例外は、致命的ではないエラー呼び出しスタックから放出されないので、この関数の後に強制的に使用して、それを使用する統一的な方法を提供します。

function superTryCatchFinallyAndExit( Closure $try, Closure $catch = NULL, Closure $finally )
{
    $finished = FALSE;
    register_shutdown_function( function() use ( &$finished, $catch, $finally ) {
        if( ! $finished ) {
            $finished = TRUE;
            print "EXPLODE!".PHP_EOL;
            if( $catch ) {
                superTryCatchFinallyAndExit( function() use ( $catch ) {
                    $catch( new Exception( "Fatal Error!!!" ) );
                }, NULL, $finally );                
            } else {
                $finally();                
            }
        }
    } );
    try {
        $try();
    } catch( Exception $e ) {
        if( $catch ) {
            try {
                $catch( $e );
            } catch( Exception $e ) {}
        }
    }
    $finished = TRUE;
    $finally();
    exit();
}

ちょうどこの解決策を思いついた(PHP 5.2.0+):

function shutDownFunction() { 
    $error = error_get_last();
    // fatal error, E_ERROR === 1
    if ($error['type'] === E_ERROR) { 
        //do your stuff     
    } 
}
register_shutdown_function('shutDownFunction');

http://www.php.net/manual/en/errorfunc.constants.php定義されているさまざまなエラータイプ


現在の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はエラーの表示を停止します。そうしないと、エラーハンドラの前にエラーテキストがクライアントに送信されます


私はPHPですべてのエラーを捕まえるための完全なソリューションでWikiスタイルのQ&Aを書きました。 ここで見る/盗む/盗む/批評hereことができhere

このソリューションには、PHPが生成できるすべてのエラーをラップする5つのメソッドが含まれています。これらのメソッドは最終的にエラーを 'ErrorHandler'型付きオブジェクトに渡します。

いくつかの人々がそれからいくつかの使用を得ることを願っています。 たとえあなたがそれを完全に盗んでいないとしても、その解決策は、あらゆる面でPHPのエラーを処理する方法の良い例であると確信しています。

@Lucas Batistussiは創造性のためのポイントを得ました - 私は私の解決策も共有することができ、いくつかの同様の点で撃つことができると思いました...


私は、プロダクションで致命的なエラーを処理する代わりに、静的なスタイルの503 Service Unavailable HTML出力を表示する必要があります。 これは確かに "致命的なエラーをキャッチする"合理的なアプローチです。 これは私がやったことです:

私は任意のE_ERROR、E_USER_ERRORなどに "503サービスを利用できません" HTMLページを表示するカスタムエラー処理関数 "error_handler"を持っています。これは現在、致命的エラーをキャッチしているシャットダウン関数で呼び出されます。

function fatal_error_handler() {

    if (@is_array($e = @error_get_last())) {
        $code = isset($e['type']) ? $e['type'] : 0;
        $msg = isset($e['message']) ? $e['message'] : '';
        $file = isset($e['file']) ? $e['file'] : '';
        $line = isset($e['line']) ? $e['line'] : '';
        if ($code>0) error_handler($code,$msg,$file,$line);
    }
}
set_error_handler("error_handler");
register_shutdown_function('fatal_error_handler');

私のカスタムerror_handler関数で、エラーがE_ERRORまたはE_USER_ERRORならば、私は@ob_end_clean()を呼び出します。 バッファを空にして、PHPの "fatal error"というメッセージを削除します。

error_handlerスクリプトでエラーが発生しないようにするため、厳密なisset()検査と@ silencing関数に注意してください。

依然としてケパロに同意すると、致命的なエラーをキャッチすることは、「致命的なエラー」の目的を無効にするので、それ以上の処理を行うことを意図していません。 このシャットダウン処理では、メールサーバーまたは受信トレイを確実にバックアップするため、mail()関数を実行しないでください。 これらのエラーをファイルに記録し、cronをスケジュールしてこれらのerror.logファイルを見つけて管理者にメールしてください。


致命的なエラーでさえも捕らえなければならない特定の状況があります(正常に終了する前にいくつかのクリーンアップを行う必要があります。 私は電子メールを介して私の致命的なエラーを得ることができるように私のcodeigniterアプリケーションでpre_systemフックを実装し、これは私が報告されなかったバグを見つけるのを助けた(または、彼らがすでにそれらについて知っていたように修正された後に報告された:))。 Sendemailは、エラーが既に報告されているため、既知のエラーで複数回あなたをスパムしないようにチェックします。

class PHPFatalError {

    public function setHandler() {
        register_shutdown_function('handleShutdown');
    }

}

function handleShutdown() {
    if (($error = error_get_last())) {
        ob_start();
        echo "<pre>";
        var_dump($error);
        echo "</pre>";
        $message = ob_get_clean();
        sendEmail($message);
        ob_start();
        echo '{"status":"error","message":"Internal application error!"}';
        ob_flush();
        exit();
    }
}




fatal-error