PHP 치명적인 (`E_ERROR`) 오류를 어떻게 잡을 수 있습니까?


557

set_error_handler()대부분의 PHP 오류를 잡는 데 사용할 수 있지만 E_ERROR존재하지 않는 함수 호출과 같은 치명적인 ( ) 오류에는 작동하지 않습니다. 이러한 오류를 잡는 다른 방법이 있습니까?

나는 호출에 노력하고 mail()모든 오류 및 PHP 5.2.3을 실행하고 있습니다.


PHP의 모든 오류를 잡기위한 완벽한 솔루션으로 위키 스타일의 Q & A를 작성했습니다. Stack Overflow에서 보거나 줍거나 도난 당했을 수 있습니다 . 이 솔루션에는 PHP가 생성 할 수있는 모든 오류를 래핑하는 5 가지 방법이 포함되어 있으며,이 오류는 결국 해당 오류를 'ErrorHandler'형식의 객체로 전달합니다.
DigitalJedi805



답변:


635

register_shutdown_functionPHP 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함수 를 작성하십시오 .

또한보십시오:


113
+1 실제 정답입니다. 나는 사람들이 왜 "치명적인 실수로부터 회복 할 수 없다"고 끊어 졌는지 모르겠다. 문제는 회복에 대해 아무 말도하지 않았다.
David Harkness

21
고마워요 치명적인 오류 (예 : 메모리 제한)를 복구하는 것은 내가 시도하려는 것이 아니지만 고객이 지원 티켓을 제출하지 않고 이러한 오류를 발견 할 수있게 만드는 것은 모든 차이를 만듭니다.
Ilija

2
기본 메일 사용 :mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser

4
스크립트 종료가 종료 될 때마다 종료 기능이 호출되므로 @ScottNicol Slava V가 정확합니다. 코드 작성 방법에 따라 모든 페이지로드시 이메일이 전송됩니다.
Nate

2
참고 : 이것은 100 % 정답이 아닙니다. @ 기호를 사용하여 오류를 무시하는 모든 위치는 여전히 마지막 오류를 설정하므로 오류를 처리 할 수 ​​있습니다. 따라서 스크립트는 문제없이 완료되지만 register_shutdown_function은 여전히 ​​오류가 발생했다고 생각합니다. PHP 7부터 만 error_clear_last () 함수가 있습니다.
Rahly

150

방금이 솔루션 (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');

미리 정의 된 상수에 다른 오류 유형이 정의되어 있습니다.


25
이 솔루션은 최고 등급의 답변보다 훨씬 더 많은 기능을 제공합니다. 최상위 답변은 오류가없는 경우에도 스크립트가 실행될 때마다 이메일을 보냅니다. 이것은 치명적인 오류로 엄격하게 실행됩니다.
kmoney12

@periklis, 마지막 오류가 이미 처리 된 경우 error_get_last가 여전히 반환하지 않습니까?
Pacerier

@Pacerier 오류가 예외는 아니기 때문에 "처리 된"의 의미를 잘 모르겠지만 대답이 "예"라고 생각합니다
periklis

3
@Pacerier 제 생각에는 흥미로운 질문입니다. php.net/error_get_last를 살펴보십시오 . 댓글 중 하나는 " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis

1
아마도 이것은 전화 register_shutdown_function()가 치명적인 오류보다 빠르다 는 것이 분명 합니다. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');예상대로 작동하지 않습니다.
Nobu

117

PHP는 치명적인 오류를 포착하고 복구하는 일반적인 방법을 제공하지 않습니다. 치명적인 오류 후에 처리를 복구하지 않아야하기 때문입니다. 출력 버퍼와 일치하는 문자열 (PHP.net에 기술 된 원본 게시물에서 제안한 바와 같이)은 잘못 권장됩니다. 단순히 신뢰할 수 없습니다.

에러 핸들러 메소드 내에서 mail () 함수를 호출하는 것도 문제가있는 것으로 판명되었습니다. 많은 오류가있는 경우 메일 서버에 작업이로드되고 불완전한받은 편지함을 찾을 수 있습니다. 이를 피하기 위해 cron을 실행하여 주기적으로 오류 로그를 스캔하고 그에 따라 알림을 보내는 것을 고려할 수 있습니다. Nagios 와 같은 시스템 모니터링 소프트웨어를 살펴볼 수도 있습니다 .


종료 기능을 등록하는 방법에 대해 설명하려면 :

셧다운 기능을 등록 할 수 있다는 것은 사실이며, 정답입니다.

여기서 중요한 점은 일반적으로 출력 버퍼에 정규식을 사용하여 치명적인 오류를 복구하려고 시도하지 않아야한다는 것입니다. 나는 php.net에 대한 제안과 연결되거나 변경 된 수락 된 답변 에 응답하고있었습니다 .

그 제안은 예외 처리 중에 출력 버퍼에 대해 정규 표현식을 사용하는 것이며 치명적인 오류 (예상치 않은 구성 오류 텍스트와 일치하는 것으로 감지)의 경우 일종의 복구 또는 지속적인 처리를 시도하십시오. 그것은 권장되는 관행이 아닐 것입니다 (저는 원래 제안도 찾을 수없는 이유라고 생각합니다. 간과하거나 PHP 커뮤니티가 제안하지 않았습니다).

출력 버퍼링 콜백이 호출되기 전에 최신 버전의 PHP (약 5.1)가 셧다운 기능을 더 일찍 호출하는 것 같습니다. 버전 5 이하에서는 그 순서가 역순이었습니다 (출력 버퍼링 콜백에 종료 기능이 뒤 따름). 또한 약 5.0.5 (질문의 버전 5.2.3보다 훨씬 빠름) 이후 등록 된 종료 기능이 호출되기 전에 객체가 언로드되므로 메모리 내 객체를 사용하여 수행 할 수 없습니다. 많은 것.

따라서 셧다운 기능을 등록하는 것은 좋지만 셧다운 기능으로 수행해야하는 작업 종류는 아마도 몇 가지 부드러운 셧다운 절차로 제한 될 수 있습니다.

여기서 중요한 것은이 질문을 우연히 발견하고 원래 받아 들여진 대답의 조언을 보는 사람에게는 지혜로운 말입니다. 출력 버퍼를 정규식으로 변환하지 마십시오.


25
Pfff, 다음 날 아침에받은 650.000 개 이상의 이메일을 기억합니다. 그 이후 내 ErrorHandler는 웹 서버 당 100 개의 이메일로 제한됩니다.
Bob Fanger

14
그건 사실이 아니야. register_shutdown_function으로 치명적 오류를 캡처 할 수 있습니다.
hipertracker

56
치명적인 오류를 포착하려는 사용 사례가 있습니다. 예를 들어 테스트 스위트는 실패했을 때 멈추지 않아야하며 치명적인 오류를보고하고 다음 테스트로 넘어 가야합니다. PHP는 너무 많은 것들을 "치명적인"오류로 만듭니다.
Chad

24
네, "잡아서는 안된다"는 말이 너무 짧습니다. 생산 시스템에서는 필요 무언가가 (- 기본 PHP 오류 처리가 매우 정교한없는 이메일을 설정하거나 데이터베이스에 물건을 로그) 실패 할 때를 알고.
BT

8
나는 "오류를 잡아서 해결할 수 있도록 오류를 잡아야한다"... Ini 지시문 ini log_errors 및 error_log에 대해 당신이 말하는 것에 대해 간단히 말하고 싶습니다.
Kelly Elton

37

글쎄, 다른 방법으로 치명적인 오류를 잡을 수있는 것 같습니다 :)

ob_start('fatal_error_handler');

function fatal_error_handler($buffer){
    $error = error_get_last();
    if($error['type'] == 1){
        // Type, message, file, line
        $newBuffer='<html><header><title>Fatal Error </title></header>
                      <style>
                    .error_content{
                        background: ghostwhite;
                        vertical-align: middle;
                        margin:0 auto;
                        padding: 10px;
                        width: 50%;
                     }
                     .error_content label{color: red;font-family: Georgia;font-size: 16pt;font-style: italic;}
                     .error_content ul li{ background: none repeat scroll 0 0 FloralWhite;
                                border: 1px solid AliceBlue;
                                display: block;
                                font-family: monospace;
                                padding: 2%;
                                text-align: left;
                      }
                      </style>
                      <body style="text-align: center;">
                        <div class="error_content">
                             <label >Fatal Error </label>
                             <ul>
                               <li><b>Line</b> ' . $error['line'] . '</li>
                               <li><b>Message</b> ' . $error['message'] . '</li>
                               <li><b>File</b> ' . $error['file'] . '</li>
                             </ul>

                             <a href="javascript:history.back()"> Back </a>
                        </div>
                      </body></html>';

        return $newBuffer;
    }
    return $buffer;
}

3
내가 할 수 있다면 나는이 10 개의 공감대를 줄 것이다. 페이지 폭탄이 있거나 아무것도 기록되지 않을 때 때때로 발생하는 이상한 오류에 대해 완벽하게 작동합니다. 라이브 프로덕션 코드에서는 사용하지 않지만 실패한 것에 대한 빠른 답변이 필요할 때 페이지에 추가하는 것이 좋습니다. 감사합니다!
Night Owl

인터넷에서 찾은 최고의 솔루션 중 하나입니다. 매력처럼 작동합니다.
반송

어떤 방법으로? 인터넷상에서 최상의 솔루션 중 하나 인 경우 설명이 순서대로 표시됩니다 (더 나아질 수 있음).
피터 Mortensen

예를 들어 모든 CSS 컨텐츠가 필요합니까? 필수품으로 줄일 수 없습니까? 여기에 주석이 아닌 답변을 편집하여 응답하십시오 (적절한 경우).
피터 모텐슨

@PeterMortensen 나는 최선을 주장하지 않습니다. 또한 문제에 대한 나의 개인적인 해결책은 훨씬 더 전문적인 다른 더 나은 옵션이 있습니다. 누군가가 제안한대로 생산에 적합하지 않습니다. CSS는 방금 내 개인 코드를 잘라 붙여 넣은 bcz입니다
sakhunzai

36

치명적 오류 또는 복구 가능한 치명적 오류는 이제 PHP 7 이상의 버전Error 에서 인스턴스를 발생시킵니다 . 다른 예외와 마찬가지로 블록을 사용하여 객체를 잡을 수 있습니다 .Errortry/catch

예:

<?php
$variable = 'not an object';

try {
    $variable->method(); // Throws an Error object in PHP 7 or higger.
} catch (Error $e) {
    // Handle error
    echo $e->getMessage(); // Call to a member function method() on string
}

https://3v4l.org/67vbk

또는 Throwable인터페이스를 사용 하여 모든 예외를 포착 할 수 있습니다 .

예:

<?php
    try {
        undefinedFunctionCall();
    } catch (Throwable $e) {
        // Handle error
        echo $e->getMessage(); // Call to undefined function undefinedFunctionCall()
    }

https://3v4l.org/Br0MG

자세한 정보 : http://php.net/manual/en/language.errors.php7.php


2
이것을 사용할 Fatal error: Trait 'FailedTrait' not found in때 와 같은 오류를 잡기 위해 이것을 사용하는 방법에 대한 어떤 아이디어 ReflectionClass?
TCB13

1
@ TCB13은 try 내부 내용을 파일로 감싸고 include "filename.php"대신 try블록 으로 감싸고 블록을 Throwable잡으십시오 ParseError.
Niloct

24

PHP에서 모든 오류 유형을 잡는 방법을 개발했습니다 (거의 모든 것)! E_CORE_ERROR에 대해 잘 모르겠습니다 (해당 오류에 대해서만 작동하지는 않습니다). 그러나 다른 치명적 오류 (E_ERROR, E_PARSE, E_COMPILE ...)의 경우 하나의 오류 처리기 함수 만 사용하면 제대로 작동합니다! 내 해결책이 있습니다.

다음 코드를 기본 파일 (index.php)에 넣으십시오.

<?php
    define('E_FATAL',  E_ERROR | E_USER_ERROR | E_PARSE | E_CORE_ERROR |
            E_COMPILE_ERROR | E_RECOVERABLE_ERROR);

    define('ENV', 'dev');

    // Custom error handling vars
    define('DISPLAY_ERRORS', TRUE);
    define('ERROR_REPORTING', E_ALL | E_STRICT);
    define('LOG_ERRORS', TRUE);

    register_shutdown_function('shut');

    set_error_handler('handler');

    // Function to catch no user error handler function errors...
    function shut(){

        $error = error_get_last();

        if($error && ($error['type'] & E_FATAL)){
            handler($error['type'], $error['message'], $error['file'], $error['line']);
        }

    }

    function handler( $errno, $errstr, $errfile, $errline ) {

        switch ($errno){

            case E_ERROR: // 1 //
                $typestr = 'E_ERROR'; break;
            case E_WARNING: // 2 //
                $typestr = 'E_WARNING'; break;
            case E_PARSE: // 4 //
                $typestr = 'E_PARSE'; break;
            case E_NOTICE: // 8 //
                $typestr = 'E_NOTICE'; break;
            case E_CORE_ERROR: // 16 //
                $typestr = 'E_CORE_ERROR'; break;
            case E_CORE_WARNING: // 32 //
                $typestr = 'E_CORE_WARNING'; break;
            case E_COMPILE_ERROR: // 64 //
                $typestr = 'E_COMPILE_ERROR'; break;
            case E_CORE_WARNING: // 128 //
                $typestr = 'E_COMPILE_WARNING'; break;
            case E_USER_ERROR: // 256 //
                $typestr = 'E_USER_ERROR'; break;
            case E_USER_WARNING: // 512 //
                $typestr = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: // 1024 //
                $typestr = 'E_USER_NOTICE'; break;
            case E_STRICT: // 2048 //
                $typestr = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: // 4096 //
                $typestr = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: // 8192 //
                $typestr = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: // 16384 //
                $typestr = 'E_USER_DEPRECATED'; break;
        }

        $message =
            '<b>' . $typestr .
            ': </b>' . $errstr .
            ' in <b>' . $errfile .
            '</b> on line <b>' . $errline .
            '</b><br/>';

        if(($errno & E_FATAL) && ENV === 'production'){

            header('Location: 500.html');
            header('Status: 500 Internal Server Error');

        }

        if(!($errno & ERROR_REPORTING))
            return;

        if(DISPLAY_ERRORS)
            printf('%s', $message);

        //Logging error on php file error log...
        if(LOG_ERRORS)
            error_log(strip_tags($message), 0);
    }

    ob_start();

    @include 'content.php';

    ob_end_flush();
?>

2
@include 'content.php'줄은 무엇입니까?
Marco

22

치명적인 오류는 포착 / 처리 할 수 ​​없지만 기록 /보고 할 수는 있습니다. 빠른 디버깅을 위해이 간단한 코드에 대한 하나의 답변을 수정했습니다.

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');

그러나이 코드는 어디로 갈까요?
TKoL

@TKoL 첫 줄. 기본적으로 스크립트 / 프로그램의 입력 파일이므로 먼저 실행합니다. 가능하지 않은 경우 공통 파일에 저장하십시오
zainengineer

17

다음과 같이 등록 된 종료 기능 내에서 예외를 throw 할 수 없습니다.

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           throw new Exception("fatal error");
        }
    }

    try {
        $x = null;
        $x->method()
    } catch(Exception $e) {
        # This won't work
    }
?>

그러나 요청을 캡처하여 다른 페이지로 리디렉션 할 수 있습니다.

<?php
    function shutdown() {
        if (($error = error_get_last())) {
           ob_clean();
           # Report the event, send email, etc.
           header("Location: http://localhost/error-capture");
           # From /error-capture. You can use another
           # redirect, to e.g. the home page
        }
    }
    register_shutdown_function('shutdown');

    $x = null;
    $x->method()
?>

11

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 your error handler
    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
    }
?>

9

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;
    }

    // [...]
}

9

정적 스타일의 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의 "치명적인 오류"메시지를 제거합니다.

@error_handler 스크립트가 오류를 생성하지 않도록하기 위해 엄격한 isset () 확인 및 침묵 함수에 유의하십시오 .

여전히 keparo에 동의하면서 치명적 오류를 발견하면 "FATAL 오류"의 목적을 무효화하므로 추가 처리를 수행 할 의도는 없습니다. 메일 서버 또는받은 편지함을 확실히 백업하므로이 종료 프로세스에서 mail () 함수를 실행하지 마십시오. 대신 이러한 발생을 파일에 기록하고 cron 작업을 예약하여 이러한 error.log 파일 을 찾아서 관리자에게 우편으로 보내십시오.


7

PHP에는 치명적인 오류가 있습니다. 그것들은 E_RECOVERABLE_ERROR로 정의됩니다. PHP 매뉴얼은 E_RECOVERABLE_ERROR를 다음과 같이 설명합니다.

잡을 수있는 치명적인 오류. 위험한 오류가 발생했지만 엔진을 불안정한 상태로 두지 않았습니다. 사용자 정의 핸들에 의해 오류가 발견되지 않으면 ( set_error_handler () 참조 ) 응용 프로그램은 E_ERROR이므로 중단됩니다.

set_error_handler ()를 사용 하고 E_RECOVERABLE_ERROR를 확인 하여 이러한 "치명적인"오류를 "잡을"수 있습니다 . 이 오류가 발생했을 때 예외를 발생시키는 것이 유용하다는 것을 알았습니다. 그러면 try / catch를 사용할 수 있습니다.

이 질문과 답변은 유용한 예를 제공합니다 . PHP 유형 힌트에서 "catchable fatal error"를 어떻게 잡을 수 있습니까?

그러나 E_ERROR 오류는 처리 할 수 ​​있지만 엔진이 불안정한 상태에서는 복구 할 수 없습니다.


6

여기에 현재 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) {

            // It is a 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는 오류를 표시하지 않습니다. 그렇지 않으면 오류 처리기 전에 오류 텍스트가 클라이언트로 전송됩니다.


1
때문에 라인 위해서는 ini_set ( 'display_errors를'거짓)의이 Upvoted;
Sahib Khan

어떤 이유로이 비트는 여전히 심지어 PHP 오류가 표시됩니다 그것을 경우 당신은 그것을 다르게 처리
나리 칸

5

여기에있는 대부분의 답변은 불필요하게 장황하기 때문에 다음은 가장 투표가 많은 답변입니다.

function errorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = array()) {
    //Do stuff: mail, log, etc
}

function fatalHandler() {
    $error = error_get_last();
    if($error) errorHandler($error["type"], $error["message"], $error["file"], $error["line"]);
}

set_error_handler("errorHandler")
register_shutdown_function("fatalHandler");

4

실제로는 아닙니다. 치명적이므로 치명적 오류라고합니다. 당신은 그들로부터 회복 할 수 없습니다.


12
잡기와 회복은 매우 다른 두 가지입니다.
Simon Forsberg

3

치명적인 오류를 일으킬 수있는 코드를 "샌드 박스"할 수 있도록이 기능을 개발했습니다. 클로저에서 발생하는 예외 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();
}

3

치명적인 오류조차도 포착해야하는 특정 상황이 있습니다 (정상적으로 종료하기 전에 약간의 정리를 수행해야하며 죽지 않아야합니다.).

전자 메일을 통해 치명적인 오류를 얻을 수 있도록 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();
    }
}

"Sendemail" 는 무엇입니까 ? Sendmail 을 의미 합니까 (댓글이 아닌 답변수정하여 응답 )?
Peter Mortensen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.