¿Cómo puedo detectar un error fatal de PHP (`E_ERROR`)?

557

Puedo usar set_error_handler()para detectar la mayoría de los errores de PHP, pero no funciona para E_ERRORerrores fatales ( ), como llamar a una función que no existe. ¿Hay otra forma de detectar estos errores?

Estoy tratando de llamar mail()a todos los errores y estoy ejecutando PHP 5.2.3.

demasiado php
fuente
Escribí un Q&A de estilo wiki con una solución completa para detectar todos los errores en PHP; que se puede ver / recoger / robar / criticar aquí en Stack Overflow . La solución incluye cinco métodos que envuelven todos los errores que PHP puede generar que eventualmente pasarán dichos errores a un objeto tipeado 'ErrorHandler'.
DigitalJedi805
Ver también: stackoverflow.com/questions/1087365/…
dreftymac
Ver también: bugs.php.net/bug.php?id=41418
dreftymac
Consulte también: stackoverflow.com/questions/7116995
dreftymac el

Respuestas:

635

Registre errores fatales utilizando el register_shutdown_function, que requiere 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));
    }
}

Deberá definir las funciones error_maily format_error. Por ejemplo:

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 Swift Mailer para escribir la error_mailfunción.

Ver también:

usuario259973
fuente
113
+1 Esta es la respuesta correcta real . No sé por qué la gente está obsesionada con "no puedes recuperarte de errores fatales": la pregunta no decía nada sobre la recuperación.
David Harkness el
21
Gracias buena. Recuperarme de errores fatales (límites de memoria, por ejemplo) no es algo que intente hacer, pero hacer que estos errores sean detectables (sin que el cliente envíe un ticket de soporte) hace toda la diferencia.
Ilija
2
Usando correo básico:mail("[email protected]", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Eric Muyser
44
@ScottNicol Slava V es correcto, porque la función de apagado se llama cada vez que el script termina de ejecutarse. Con la forma en que se escribe el código ahora, se enviará un correo electrónico en CADA carga de página.
Nate
2
Nota: esta no es una respuesta 100% correcta. Cualquier lugar que use un símbolo @ para ignorar los errores aún CONFIGURARÁ el último error (para que pueda manejar los errores). Por lo tanto, su secuencia de comandos termina sin problemas, pero la función register_shutdown_function aún cree que ocurrió un error. Sin embargo, solo desde PHP 7 han tenido una función error_clear_last ().
Rahly
150

Se me ocurrió esta solución (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');

Se definen diferentes tipos de error en Constantes predefinidas .

periklis
fuente
25
Esta solución hace mucho más para mí que la respuesta mejor calificada. La respuesta mejor calificada le enviará un correo electrónico cada vez que se ejecute el script, incluso si no hay ningún error. Este se ejecuta estrictamente en un error fatal.
kmoney12
@periklis, si el último error ya se manejó, error_get_last aún devolvería, ¿no?
Pacerier
@Pacerier No estoy seguro de lo que quieres decir con "manejado", ya que los errores no son excepciones, pero supongo que la respuesta es "sí"
periklis
3
@Pacerier Ya veo, esa es una pregunta interesante. Eche un vistazo a php.net/error_get_last , uno de los comentarios 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
Quizás esto es obvio, la llamada register_shutdown_function()debe ser anterior a cualquier error fatal. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction');No funcionará como se esperaba.
Nobu
117

PHP no proporciona medios convencionales para atrapar y recuperarse de errores fatales. Esto se debe a que el procesamiento no debe recuperarse normalmente después de un error fatal. La cadena que coincide con un búfer de salida (como sugiere la publicación original, la técnica descrita en PHP.net) es definitivamente desaconsejada. Es simplemente poco confiable.

Llamar a la función mail () desde un método de manejo de errores también resulta problemático. Si tuviera muchos errores, su servidor de correo estaría cargado de trabajo y podría encontrarse con una bandeja de entrada retorcida. Para evitar esto, puede considerar ejecutar un cron para escanear registros de errores periódicamente y enviar notificaciones en consecuencia. También es posible que desee analizar el software de monitoreo del sistema, como Nagios .


Para hablar un poco sobre el registro de una función de apagado:

Es cierto que puede registrar una función de apagado, y esa es una buena respuesta.

El punto aquí es que normalmente no deberíamos intentar recuperarnos de errores fatales, especialmente no mediante el uso de una expresión regular contra su búfer de salida. Estaba respondiendo a la respuesta aceptada , que se vinculaba con una sugerencia en php.net que desde entonces ha sido modificada o eliminada.

Esa sugerencia fue usar una expresión regular contra el búfer de salida durante el manejo de excepciones, y en el caso de un error fatal (detectado por la coincidencia con cualquier texto de error configurado que pueda esperar), intente hacer algún tipo de recuperación o procesamiento continuo. Esa no sería una práctica recomendada (creo que es por eso que no puedo encontrar la sugerencia original también. O lo estoy pasando por alto o la comunidad de php la rechazó).

Vale la pena señalar que las versiones más recientes de PHP (alrededor de 5.1) parecen llamar a la función de apagado antes, antes de que se invoque la devolución de llamada de búfer de salida. En la versión 5 y anteriores, ese orden era el inverso (la devolución de llamada de la memoria intermedia de salida fue seguida por la función de apagado). Además, desde aproximadamente 5.0.5 (que es mucho anterior a la versión 5.2.3 del interlocutor), los objetos se descargan mucho antes de que se llame a una función de apagado registrada, por lo que no podrá confiar en sus objetos en memoria para hacer mucho de cualquier cosa.

Por lo tanto, registrar una función de apagado está bien, pero el tipo de tareas que debe realizar una función de apagado probablemente se limite a un puñado de procedimientos de apagado suaves.

La clave aquí es solo algunas palabras de sabiduría para cualquiera que se tope con esta pregunta y vea el consejo en la respuesta originalmente aceptada. No regexes tu búfer de salida.

keparo
fuente
25
Pfff, recuerdo esos 650.000 correos electrónicos que recibí a la mañana siguiente. Desde entonces, mi ErrorHandler está limitado a 100 correos electrónicos por servidor web.
Bob Fanger
14
Eso no es cierto. Puede capturar errores fatales con la función register_shutdown_function.
hipertracker
56
Existen casos de uso para querer detectar errores fatales. Las suites de prueba, por ejemplo, no solo deben detenerse cuando uno falla, sino que deben informar el error fatal y pasar a la siguiente prueba. PHP simplemente comete demasiados errores "fatales".
Chad
24
Sí, decir que "no deberían ser atrapados" es muy miope. En un sistema de producción, debe saber cuándo algo falla (configurar correos electrónicos o registrar cosas en una base de datos; el manejo predeterminado de errores de php no es muy sofisticado).
BT
8
Quiero hacer un comentario rápido sobre lo que está diciendo sobre "Los errores deben detectarse para que podamos solucionarlos" ... Directivas Ini ini log_errors y error_log.
Kelly Elton
37

Bueno, parece posible detectar errores fatales de otra manera :)

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;
}
sakhunzai
fuente
3
Daría estos 10 votos a favor si pudiera. Funciona perfectamente para mí en esos errores extraños que a veces ocurren cuando una página bombardea y no se registra nada. No lo usaría en el código de producción en vivo, pero es genial agregarlo a una página cuando se necesita una respuesta rápida a lo que falla. ¡Gracias!
Night Owl
Una de las mejores soluciones que he encontrado en Internet. Funciona como encanto.
Rebote
¿En qué manera? Una explicación estaría en orden, especialmente si es una de las mejores soluciones en Internet (podría ser aún mejor).
Peter Mortensen
¿Es necesario, por ejemplo, todo el contenido CSS? ¿No podría reducirse a lo esencial? Responda editando su respuesta, no aquí en los comentarios (según corresponda).
Peter Mortensen
@PeterMortensen No reclamo lo mejor. También es mi solución personal al problema, hay otras mejores opciones mucho más profesionales. Según lo sugerido por alguien, no es adecuado para la producción. Css está ahí porque acabo de pegar mi código personal
sakhunzai
36

Los errores fatales o errores fatales recuperables Ahora lanzan casos de Erroren PHP 7 o versiones superiores . Como cualquier otra excepción, los Errorobjetos se pueden atrapar usando un try/catchbloque.

Ejemplo:

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

O puede usar la Throwableinterfaz para capturar todas las excepciones.

Ejemplo:

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

https://3v4l.org/Br0MG

Para más información: http://php.net/manual/en/language.errors.php7.php

LugiHaue
fuente
2
¿Alguna idea sobre cómo usar esto para detectar un error como Fatal error: Trait 'FailedTrait' not found incuando se usa ReflectionClass?
TCB13
1
@ TCB13 intente envolver el contenido interno de prueba en un archivo y, include "filename.php"en su lugar, en el trybloque, luego el Throwablebloque catch al menos funciona ParseError.
Niloct
24

¡Desarrollé una forma de detectar todos los tipos de error en PHP (casi todos)! ¡No estoy seguro acerca de E_CORE_ERROR (creo que no funcionará solo para ese error)! Pero, para otros errores fatales (E_ERROR, E_PARSE, E_COMPILE ...) funciona bien usando solo una función de controlador de errores. Ahí va mi solución:

Ponga el siguiente código en su archivo 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();
?>
Lucas Batistussi
fuente
2
¿Qué hace la línea @include 'content.php'?
Marco
22

No puede detectar / manejar errores fatales, pero puede registrarlos / informarlos. Para una depuración rápida, modifiqué una respuesta a este código simple

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');
zainengineer
fuente
pero ¿a dónde iría este código?
TKoL
@TKoL primera línea. Básicamente, el archivo de entrada de su script / programa, por lo que se ejecuta primero, si eso no es posible, póngalo en un archivo común
zainengineer
17

No puede lanzar una excepción dentro de una función de apagado registrada como esa:

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

Pero puede capturar y redirigir la solicitud a otra 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()
?>
hipertracker
fuente
11

Si está utilizando PHP> = 5.1.0 Simplemente haga algo como esto con la clase 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
    }
?>
Cyril Tata
fuente
9

Buena solución encontrada en 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);
    }
}

Esta clase le permite comenzar lo específico a ErrorHandlerveces si lo necesita. Y luego también puede detener el controlador.

Use esta clase, por ejemplo, así:

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;

Enlace al código de clase completo:
https://github.com/zendframework/zf2/blob/master/library/Zend/Stdlib/ErrorHandler.php


Una solución quizás mejor es la de Monolog :

Enlace al código de clase completo:
https://github.com/Seldaek/monolog/blob/master/src/Monolog/ErrorHandler.php

También puede manejar FATAL_ERRORS usando la register_shutdown_functionfunción. De acuerdo con esta clase, un FATAL_ERROR es uno de los siguientes 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;
    }

    // [...]
}
algoritmo
fuente
9

Necesito manejar errores fatales para que la producción muestre en su lugar una salida HTML no disponible de servicio 503 de estilo estático . Este es sin duda un enfoque razonable para "detectar errores fatales". Esto es lo que he hecho:

Tengo una función personalizada de manejo de errores "error_handler" que mostrará mi página HTML "503 servicio no disponible" en cualquier E_ERROR, E_USER_ERROR, etc. Esto ahora se llamará en la función de apagado, detectando mi error 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');

en mi función personalizada error_handler, si el error es E_ERROR, E_USER_ERROR, etc. También llamo @ob_end_clean();para vaciar el búfer, eliminando así el mensaje de "error fatal" de PHP.

Tome nota importante de las estrictas funciones de comprobación y @silenciamiento de isset () ya que no queremos que nuestros scripts error_handler generen errores.

Al seguir de acuerdo con Keparo, detectar errores fatales no cumple con el propósito del "error FATAL", por lo que no está destinado a que realices un procesamiento posterior. No ejecute ninguna función de correo () en este proceso de apagado, ya que seguramente hará una copia de seguridad del servidor de correo o de su bandeja de entrada. En su lugar, registre estas ocurrencias en el archivo y programe un trabajo cron para encontrar estos archivos error.log y envíelos por correo a los administradores.

Prof83
fuente
7

PHP tiene errores fatales detectables. Se definen como E_RECOVERABLE_ERROR. El manual de PHP describe un E_RECOVERABLE_ERROR como:

Error fatal capturable. Indica que ocurrió un error probablemente peligroso, pero no dejó el motor en un estado inestable. Si el error no es detectado por un identificador definido por el usuario (consulte también set_error_handler () ), la aplicación se anula ya que era un E_ERROR.

Puede "atrapar" estos errores "fatales" utilizando set_error_handler () y buscando E_RECOVERABLE_ERROR. Me resulta útil lanzar una excepción cuando se detecta este error, entonces puede usar try / catch.

Esta pregunta y respuesta proporcionan un ejemplo útil: ¿Cómo puedo detectar un "error fatal detectable" en las sugerencias de tipo PHP?

Los errores E_ERROR, sin embargo, pueden manejarse, pero no recuperarse ya que el motor está en un estado inestable.

Ninguna
fuente
6

Aquí hay un buen truco para obtener el método actual 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;
        }
    }
?>

También quiero señalar que si llamas

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

PHP deja de mostrar el error. De lo contrario, el texto del error se enviará al cliente antes de su controlador de errores.

Sander Visser
fuente
1
Ha votado a favor debido a la línea ini_set ('display_errors', false);
Sahib Khan
Si por alguna razón este bit está activado, seguirá mostrando errores de php, incluso si lo manejas de manera diferente
Sahib Khan el
5

Como la mayoría de las respuestas aquí son innecesariamente detalladas, aquí está mi versión no fea de la respuesta más 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");
Mahn
fuente
4

Realmente no. Los errores fatales se llaman así, porque son fatales. No puedes recuperarte de ellos.

troelskn
fuente
12
atrapar y recuperarse son dos cosas muy diferentes.
Simon Forsberg
3

Desarrollé esta función para hacer posible el código "sandbox" que podría causar un error fatal. Dado que las excepciones lanzadas desde el cierre register_shutdown_functionno se emiten desde la pila de llamadas de error pre-fatal, me veo obligado a salir después de esta función para proporcionar una forma uniforme de usarla.

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();
}
Kendall Hopkins
fuente
3

Hay ciertas circunstancias en las que se deben detectar incluso los errores fatales (es posible que deba limpiar un poco antes de salir con gracia y no simplemente morir ...).

He implementado un enlace pre_system en mis aplicaciones CodeIgniter para que pueda recibir mis errores fatales a través de correos electrónicos, y esto me ayudó a encontrar errores que no se informaron (o se informaron después de que se corrigieron, como ya sabía sobre ellos :)).

Sendemail comprueba si el error ya se ha informado para que no le envíe spam con errores conocidos varias veces.

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();
    }
}
tix3
fuente
¿Qué es "Sendemail" ? ¿Te refieres a Sendmail (responde editando tu respuesta , no aquí en los comentarios)?
Peter Mortensen