Como eu pego um erro fatal do PHP (`E_ERROR`)?


557

Posso usar set_error_handler()para capturar a maioria dos erros de PHP, mas não funciona para E_ERRORerros fatais ( ), como chamar uma função que não existe. Existe outra maneira de capturar esses erros?

Estou tentando chamar mail()todos os erros e estou executando o PHP 5.2.3.


Eu escrevi uma seção de perguntas e respostas no estilo wiki com uma solução completa para a captura de todos os erros no PHP; que pode ser visto / recolhido / roubado / criticado aqui no Stack Overflow . A solução inclui cinco métodos que agrupam todos os erros que o PHP pode gerar e que, eventualmente, passam esses erros até um objeto digitado 'ErrorHandler'.
precisa saber é o seguinte



Respostas:


635

register_shutdown_functionRegistre erros fatais usando o , que requer 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));
    }
}

Você terá que definir as funções error_maile format_error. Por exemplo:

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

Use o Swift Mailer para escrever a error_mailfunção.

Veja também:


113
+1 Esta é a resposta correta real . Não sei por que as pessoas estão se perguntando "você não pode se recuperar de erros fatais" - a questão não dizia nada sobre a recuperação.
David Harkness

21
Obrigado, bom. Recuperar erros fatais (limites de memória, por exemplo) não é algo que eu tentaria fazer, mas tornar esses erros detectáveis ​​(sem o cliente enviar um tíquete de suporte) faz toda a diferença.
Ilija

2
Usando correio básico:mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser

4
@ScottNicol Slava V está correto, porque a função de desligamento é chamada toda vez que o script termina a execução. Com a maneira como o código é gravado agora, um email será enviado em TODAS as páginas carregadas.
Nate

2
Nota: esta não é uma resposta 100% correta. Qualquer local que use um símbolo @ para ignorar erros ainda definirá o último erro (para que você possa lidar com erros). Portanto, seu script termina sem problemas, mas a função register_shutdown_ ainda acha que ocorreu um erro. Somente desde o PHP 7 eles tiveram uma função error_clear_last ().
Rahly

150

Acabei de criar esta solução (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');

Diferentes tipos de erro são definidos em constantes predefinidas .


25
Esta solução faz muito mais por mim do que a resposta mais bem avaliada. A resposta com a melhor classificação enviará um e-mail toda vez que o script for executado, mesmo se não houver erro. Este é executado estritamente em um erro fatal.
kmoney12

@periklis, se o último erro já foi tratado, error_get_last ainda retornaria, não seria?
Pacerier 12/07/2013

@Pacerier Eu não tenho certeza do que você quer dizer com "manipulados", como erros não são exceções, mas acho que a resposta é "sim"
Periklis

3
@ Pacerier eu vejo, essa é uma pergunta interessante. Dê uma olhada em php.net/error_get_last , um dos comentários menciona que " If an error handler (see set_error_handler ) successfully handles an error then that error will not be reported by this function."
periklis

1
Talvez isso seja óbvio, a chamada register_shutdown_function()deve ser anterior a qualquer erro fatal. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');não funcionará como esperado.
25716 Nobu

117

O PHP não fornece meios convencionais para capturar e recuperar erros fatais. Isso ocorre porque o processamento normalmente não deve ser recuperado após um erro fatal. String que corresponde a um buffer de saída (como sugerido na publicação original, a técnica descrita no PHP.net) é definitivamente desaconselhada. É simplesmente não confiável.

Chamar a função mail () de dentro de um método de tratamento de erros também se mostra problemático. Se você tivesse muitos erros, seu servidor de e-mail seria carregado com trabalho, e você poderia se encontrar com uma caixa de entrada desagradável. Para evitar isso, considere executar um cron para verificar os logs de erros periodicamente e enviar notificações de acordo. Você também pode procurar um software de monitoramento de sistema, como o Nagios .


Para falar um pouco sobre o registro de uma função de desligamento:

É verdade que você pode registrar uma função de desligamento, e essa é uma boa resposta.

O ponto aqui é que normalmente não devemos tentar recuperar de erros fatais, especialmente não usando uma expressão regular no seu buffer de saída. Eu estava respondendo à resposta aceita , que estava vinculada a uma sugestão no php.net que foi alterada ou removida desde então.

Essa sugestão foi usar um regex no buffer de saída durante o tratamento de exceções e, no caso de um erro fatal (detectado pela correspondência com qualquer texto de erro configurado que você estivesse esperando), tente fazer algum tipo de recuperação ou processamento contínuo. Essa não seria uma prática recomendada (acredito que é por isso que também não encontro a sugestão original. Estou ignorando ou a comunidade php diminuiu).

Pode ser interessante notar que as versões mais recentes do PHP (em torno de 5.1) parecem chamar a função shutdown anteriormente, antes que o retorno de chamada do buffer de saída seja chamado. Na versão 5 e anterior, essa ordem era inversa (o retorno de chamada do buffer de saída foi seguido pela função de desligamento). Além disso, desde o 5.0.5 (que é muito anterior à versão 5.2.3 do questionador), os objetos são descarregados muito antes de uma função de desligamento registrada ser chamada, para que você não possa confiar nos seus objetos na memória. muita coisa.

Portanto, registrar uma função de desligamento é bom, mas o tipo de tarefa que deve ser executada por uma função de desligamento provavelmente está limitado a alguns procedimentos suaves de desligamento.

A chave aqui é apenas algumas palavras de sabedoria para quem se depara com essa pergunta e vê o conselho na resposta originalmente aceita. Não regex seu buffer de saída.


25
Pfff, lembro-me daqueles mais de 650.000 emails que recebi na manhã seguinte. Desde então, o meu ErrorHandler é limitado a 100 e-mails por servidor da web.
23410 Bob Fanger

14
Isso não é verdade. Você pode capturar erros fatais com a função register_shutdown_.
hipertracker 25/09/10

56
Existem casos de uso para querer detectar erros fatais. Os conjuntos de testes, por exemplo, não devem apenas parar quando um falha, eles devem relatar o erro fatal e prosseguir para o próximo teste. O PHP comete muitos erros "fatais".
Chad

24
Sim, dizer que "não devem ser pegos" é muito míope. Em um sistema de produção, você precisa saber quando algo falha (configurar emails ou registrar coisas em um banco de dados - o tratamento padrão de erros de php não é muito sofisticado).
BT

8
Eu quero fazer um comentário rápido sobre o que você está dizendo sobre "Os erros precisam ser detectados, para que possamos corrigi-los" ... Diretivas ini ini log_errors e error_log.
22711 Kelly Elton

37

Bem, parece possível pegar erros fatais de alguma outra maneira :)

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
Eu daria 10 votos positivos se pudesse. Funciona perfeitamente para mim nesses erros estranhos que às vezes ocorrem quando uma página é bombardeada e nada está sendo registrado. Eu não usaria código de produção ao vivo, mas é ótimo adicionar uma página quando é necessária uma resposta rápida para o que está falhando. Obrigado!
Coruja-da-noite

Uma das melhores soluções que encontrei na Internet. Funciona como charme.
Rejeição

De que maneira? Uma explicação estaria em ordem, especialmente se for uma das melhores soluções da Internet (pode se tornar ainda melhor).
Peter Mortensen

Por exemplo, é necessário todo o conteúdo CSS? Não poderia ser reduzido ao essencial? Responda editando sua resposta, não aqui nos comentários (conforme apropriado).
Peter Mortensen

@PeterMortensen Não reivindico o seu melhor. Também é a minha solução pessoal para o problema, existem outras opções melhores e mais profissionais. Como sugerido por alguém, não é adequado para a produção. CSS está lá bcz Eu apenas recortei meu código pessoal
sakhunzai

36

Erros fatais ou erros fatais recuperáveis agora jogue casos de Errorem PHP 7 ou versões superiores . Como qualquer outra exceção, os Errorobjetos podem ser capturados usando um try/catchbloco.

Exemplo:

<?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

Ou você pode usar a Throwableinterface para capturar todas as exceções.

Exemplo:

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

https://3v4l.org/Br0MG

Para mais informações: http://php.net/manual/en/language.errors.php7.php


2
Alguma idéia de como usar isso para capturar um erro como Fatal error: Trait 'FailedTrait' not found inao usar ReflectionClass?
TCB13

1
@ TCB13 tente agrupar o conteúdo interno da tentativa em um arquivo e, include "filename.php"em vez disso, no trybloco, em seguida, o bloco Throwablecatch pelo menos funciona ParseError.
Niloct 1/11

24

Eu desenvolvi uma maneira de capturar todos os tipos de erro em PHP (quase todos)! Não tenho certeza sobre E_CORE_ERROR (acho que não funcionará apenas para esse erro)! Mas, para outros erros fatais (E_ERROR, E_PARSE, E_COMPILE ...) funciona bem usando apenas uma função de manipulador de erros! Lá vai a minha solução:

Coloque este código a seguir no seu arquivo principal (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
O que a linha @include 'content.php' faz?
Marco

22

Você não pode capturar / manipular erros fatais, mas pode registrá-los / relatá-los. Para depuração rápida, modifiquei uma resposta para este código simples

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

mas para onde esse código iria?
TKoL 30/03

@TKoL primeira linha. Basicamente, digite o arquivo do seu script / programa, para que seja executado primeiro, se isso não for possível, coloque-o em um arquivo comum
zainengineer

17

Você não pode lançar uma exceção dentro de uma função de desligamento registrada assim:

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

Mas você pode capturar e redirecionar solicitações para outra página.

<?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

Se você estiver usando PHP> = 5.1.0, faça algo assim com a classe 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

Ótima solução encontrada no 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);
    }
}

Essa classe permite que você inicie o específico ErrorHandleralgumas vezes, se precisar. E então você também pode parar o manipulador.

Use esta classe, por exemplo:

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;

Link para o código completo da classe:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Uma solução talvez melhor é a da Monolog :

Link para o código completo da classe:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

Ele também pode manipular FATAL_ERRORS usando a register_shutdown_functionfunção De acordo com esta classe, um FATAL_ERROR é um dos seguintes 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

Preciso lidar com erros fatais de produção para mostrar uma saída HTML 503 Serviço Indisponível com estilo estático . Esta é certamente uma abordagem razoável para "detectar erros fatais". Isto é o que eu fiz:

Eu tenho uma função personalizada de tratamento de erros "error_handler" que exibirá minha página HTML "503 service indisponível" em qualquer E_ERROR, E_USER_ERROR, etc. Isso agora será chamado na função shutdown, detectando meu erro fatal,

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

na minha função personalizada error_handler, se o erro for E_ERROR, E_USER_ERROR, etc. Eu também chamo @ob_end_clean();para esvaziar o buffer, removendo assim a mensagem "erro fatal" do PHP.

Tome nota importante da verificação estrita isset () e @ funções silenciamento pois não queremos que nossos scripts error_handler gerem erros.

Ainda concordando com o keparo, a captura de erros fatais anula o objetivo do "erro FATAL", portanto, não é realmente necessário que você faça mais processamento. Não execute nenhuma função mail () neste processo de desligamento, pois você certamente fará backup do servidor de email ou da sua caixa de entrada. Em vez disso, registre essas ocorrências para arquivar e agendar uma tarefa cron para localizar esses arquivos error.log e enviá-los por correio aos administradores.


7

O PHP possui erros fatais capturáveis. Eles são definidos como E_RECOVERABLE_ERROR. O manual do PHP descreve um E_RECOVERABLE_ERROR como:

Erro fatal captável. Indica que ocorreu um erro provavelmente perigoso, mas não deixou o mecanismo em um estado instável. Se o erro não for detectado por um identificador definido pelo usuário (consulte também set_error_handler () ), o aplicativo será interrompido como se fosse um E_ERROR.

Você pode "capturar" esses erros "fatais" usando set_error_handler () e verificando E_RECOVERABLE_ERROR. Acho útil lançar uma exceção quando esse erro é detectado, e você pode usar try / catch.

Esta pergunta e resposta fornece um exemplo útil: Como capturar um "erro fatal capturável" nas dicas do tipo PHP?

Os erros E_ERROR, no entanto, podem ser manipulados, mas não recuperados, pois o mecanismo está em um estado instável.


6

Aqui está apenas um bom truque para obter o método error_handler atual =)

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

Também quero observar que, se você ligar

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

O PHP para de exibir o erro. Caso contrário, o texto do erro será enviado ao cliente antes do seu manipulador de erros.


1
Promovido isso por causa da linha ini_set ('display_errors', false);
Sahib Khan

Se por algum motivo este bit é sobre ele ainda vai mostrar os erros php mesmo você lidar com isso de forma diferente
Sahib Khan

5

Como a maioria das respostas aqui é desnecessariamente detalhada, aqui está a minha versão não-feia da resposta mais votada:

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

Na verdade não. Os erros fatais são chamados assim, porque são fatais. Você não pode se recuperar deles.


12
pegar e recuperar são duas coisas muito diferentes.
Simon Forsberg

3

Eu desenvolvi essa função para tornar possível o código "sandbox" que poderia causar um erro fatal. Como as exceções lançadas a partir do fechamento register_shutdown_functionnão são emitidas a partir da pilha de chamadas de erro pré-fatal, sou forçado a sair após esta função para fornecer uma maneira uniforme de usá-la.

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

Há certas circunstâncias em que mesmo erros fatais devem ser detectados (talvez seja necessário fazer uma limpeza antes de sair normalmente e não morrer apenas).

Eu implementei um gancho de pré-sistema no meu CodeIgniter aplicativos para que eu possa receber meus erros fatais por e-mails, e isso me ajudou a encontrar bugs que não foram relatados (ou que foram relatados após serem corrigidos, como eu já sabia sobre eles :)).

O Sendemail verifica se o erro já foi relatado, para que ele não envie spam com erros conhecidos várias vezes.

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

O que é "Sendemail" ? Você quer dizer Sendmail (responda editando sua resposta , não aqui nos comentários)?
Peter Mortensen
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.