Як я можу виявити фатальну помилку PHP (`E_ERROR`)?


557

Я можу використовувати set_error_handler()для лову більшості помилок PHP, але це не працює для фатальних ( E_ERROR) помилок, таких як виклик функції, яка не існує. Чи є інший спосіб виявити ці помилки?

Я намагаюся закликати mail()до всіх помилок і запускаю PHP 5.2.3.


Я написав Q&A у стилі вікі з повним рішенням для виявлення всіх помилок у PHP; які можна переглядати / збирати / вкрасти / критикувати тут, на Stack Overflow . Рішення включає в себе п’ять методів, які дозволяють створити всі помилки, які може створити PHP, і в кінцевому підсумку передадуть вказані помилки об'єкту типу "ErrorHandler".
DigitalJedi805


Дивіться також: bugs.php.net/bug.php?id=41418
dreftymac

Дивіться також: stackoverflow.com/questions/7116995
dreftymac

Відповіді:


635

Журнал фатальних помилок за допомогою програми register_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_mailта format_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 Це фактично правильна відповідь. Я не знаю, чому люди зациклюються на "ви не можете оговтатися від фатальних помилок" - питання нічого не сказало про відновлення.
Девід Харкнесс

21
Спасибі, хороший. Відновлення від фатальних помилок (наприклад, обмеження пам’яті) - це не те, що я б намагався зробити, але завдяки виявленню цих помилок (без надання клієнтом квитка підтримки) все має значення.
Ілля

2
Використання основної пошти:mail("myname@myemail.com", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);
Ерік Муйзер

4
@ScottNicol Slava V є правильним, оскільки функція відключення викликається кожного разу, коли сценарій закінчується. З тим, як написано код зараз, електронний лист буде надісланий на кожному завантаженні сторінки.
Нейт

2
Примітка: це не стовідсоткова відповідь. Будь-яке місце, яке використовує символ @ для ігнорування помилок, все одно встановить останню помилку (тому ви можете впоратися з помилками). Тож ваш сценарій закінчується без проблем, але функція register_shutdown_function все ще вважає, що сталася помилка. Тільки з PHP 7, хоча вони мали функцію error_clear_last ().
Рахлі

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');не працюватиме, як очікувалося.
Нобу

117

PHP не передбачає звичайних засобів для лову та відновлення після фатальних помилок. Це пояснюється тим, що обробку не слід відновлювати після фатальної помилки. Рядок, що відповідає вихідному буфері (як це запропоновано оригінальним повідомленням, описана техніка на PHP.net), безумовно, не рекомендується Це просто ненадійно.

Виклик функції пошти () в рамках методу обробки помилок виявляється також проблематичним. Якщо у вас було багато помилок, ваш поштовий сервер був би завантажений роботою, і ви могли б опинитися з грубо вхідною скринькою. Щоб уникнути цього, ви можете запустити cron для періодичного сканування журналів помилок та відповідного надсилання повідомлень. Можливо, ви також хочете заглянути в програмне забезпечення системного моніторингу, наприклад, Nagios .


Щоб поговорити про реєстрацію функції відключення:

Це правда, що ви можете зареєструвати функцію відключення, і це хороша відповідь.

Суть у тому, що ми, як правило, не повинні намагатися відновитись після фатальних помилок, особливо не використовуючи регулярне вираження проти вихідного буфера. Я відповідав на прийняту відповідь , яка пов’язана з пропозицією на php.net, яка з тих пір була змінена або видалена.

Ця пропозиція полягала у використанні регулярного вираження проти вихідного буфера під час обробки винятків, а у випадку фатальної помилки (виявленої зіставленням із будь-яким налаштованим текстом помилки, який ви могли очікувати), спробуйте зробити якесь відновлення чи продовжувати обробку. Це не було б рекомендованою практикою (я вважаю, що тому я також не можу знайти оригінальну пропозицію. Я або її не помічаю, або php-спільнота зняла її).

Можливо, варто відзначити, що новіші версії PHP (приблизно 5.1), схоже, викликають функцію відключення раніше, перш ніж буде викликано зворотний виклик буферизації виводу. У версії 5 та новіших версіях цей порядок був зворотним (зворотний виклик буферизації виходу супроводжувався функцією відключення). Крім того, оскільки приблизно в 5.0.5 (що набагато раніше, ніж версія 5.2.3 запитувача), об'єкти вивантажуються задовго до виклику зареєстрованої функції відключення, тому ви не зможете розраховувати на свої об'єкти в пам'яті. багато чого.

Таким чином, реєстрація функції відключення є прекрасною, але тип завдань, які повинні виконуватись функцією відключення, ймовірно, обмежується кількома процедурами обережного відключення.

Ключовий висновок тут - це лише кілька слів мудрості для кожного, хто натикається на це питання і бачить поради у первісно прийнятій відповіді. Не повторно виражайте вихідний буфер.


25
Pfff, я пам'ятаю ті 650 000+ електронних листів, які я отримав наступного ранку. З тих пір мій ErrorHandler обмежений 100 електронними листами на один веб-сервер.
Боб Фангер

14
Це не правда. Ви можете зафіксувати фатальні помилки за допомогою функції register_shutdown_function.
hipertracker

56
Існують випадки використання для того, щоб хотіти виявити фатальні помилки. Наприклад, тестові набори не повинні просто зупинятися, коли один не працює, вони повинні повідомити про смертельну помилку та перейти до наступного тесту. PHP просто робить занадто багато "фатальних" помилок.
Чад

24
Так, кажучи, що їх "не слід спіймати", це дуже короткозорість. У виробничій системі вам потрібно знати, коли щось виходить з ладу (налаштовуйте електронні листи чи журнал речей у базі даних - поводження з помилками PHP за замовчуванням не дуже складне).
BT

8
Я хочу швидко прокоментувати те, що ви все говорите про "Помилки потрібно виловлювати, щоб ми могли їх виправити" ... Ini директиви ini log_errors та error_log.
Келлі Елтон

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 оновлень, якби міг. Мені ідеально підходить для тих дивних помилок, які іноді трапляються, коли сторінка бомбує і нічого не реєструється. Я б не використовував коди в реальному виробництві, але це здорово додати на сторінку, коли потрібна швидка відповідь на те, що не вдається. Дякую!
Сова

Одне з найкращих рішень, які я знайшов в Інтернеті. Працює як шарм.
Відмов

Яким чином? Пояснення було б на порядку, особливо якщо це одне з найкращих рішень в Інтернеті (воно може стати ще кращим).
Пітер Мортенсен

Чи потрібен, наприклад, весь вміст CSS? Хіба не можна було скоротити до найважливішого? Відповідайте, відредагувавши свою відповідь, а не тут у коментарях (у відповідних випадках).
Пітер Мортенсен

@PeterMortensen Я не претендую на найкраще. Крім того, це моє особисте рішення проблеми, є й інші кращі варіанти, дуже професійні. За пропозицією когось, це не дуже добре підходить для виробництва. Css is there bcz Я щойно вирізав-вставив свій особистий код
sakhunzai

36

Фатальні помилки або відновлювані фатальні помилки тепер кидають екземпляри Errorу PHP 7 або новіших версіях . Як і будь-які інші винятки, Errorоб’єкти можна спіймати за допомогою try/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 спробуйте обгорнути спробу внутрішнього вмісту у файл, а include "filename.php"замість цього в tryблок, тоді Throwableблок захоплення принаймні працює ParseError.
Нілокт

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'?
Марко

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

Ви не можете кидати виняток всередині зареєстрованої функції відключення на зразок такої:

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

Він також може працювати з FATAL_ERRORS за допомогою register_shutdown_functionфункції. Відповідно до цього класу 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 Недоступний сервіс для виведення HTML. Це, безумовно, розумний підхід до "лову фатальних помилок". Це те, що я зробив:

У мене є спеціальна функція обробки помилок "error_handler", яка відображатиме мою сторінку "503 недоступна" на будь-якій сторінці E_ERROR, E_USER_ERROR тощо. Це тепер буде викликано функцією вимкнення, що спричинить мою фатальну помилку,

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.

Візьміть важливу увагу на сувору перевірку inset () @ замовчування функцій isset оскільки ми не хочемо, щоб наші сценарії error_handler генерували будь-які помилки.

Все ще погоджуючись з кепаро, вловлювання фатальних помилок перемагає мету "FATAL помилки", тож це насправді не призначене для подальшої обробки. Не запускайте жодних функцій пошти () у цьому процесі відключення, оскільки ви, безумовно, будете робити резервну копію поштового сервера чи поштової скриньки. Швидше занотуйте ці події, щоб подати та запланувати завдання cron, щоб знайти ці файли error.log та надіслати їх адміністраторам.


7

PHP має виправдані фатальні помилки. Вони визначаються як E_RECOVERABLE_ERROR. Посібник із PHP описує E_RECOVERABLE_ERROR як:

Фатальна помилка, яку можна наздогнати. Це вказує на те, що сталася ймовірно небезпечна помилка, але двигун не залишав нестабільний стан. Якщо помилка не виявлена ​​ручкою, визначеною користувачем (див. Також set_error_handler () ), програма скасовує, як це було E_ERROR.

Ви можете "зловити" ці "фатальні" помилки, скориставшись set_error_handler () та перевіривши E_RECOVERABLE_ERROR. Мені здається корисним викинути виняток, коли ця помилка виявлена, тоді можна використовувати спробувати / catch.

Це запитання та відповідь дає корисний приклад: Як я можу виявити "підлягаючу фатальну помилку" підказки типу PHP?

Помилки 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', false);
Сахіб Хан

Якщо чомусь цей біт є, він все одно відображатиме помилки 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
вилов та відновлення - це дві дуже різні речі.
Саймон Форсберг

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

Існують певні обставини, за яких слід вловлювати навіть фатальні помилки (можливо, вам доведеться провести очищення, перш ніж виходити граціозно, а не просто вмирати ..).

Я реалізував гачок pre_system у своїх програмах CodeIgniter, щоб я міг отримати свої фатальні помилки через електронні листи, і це допомогло мені знайти помилки, про які не повідомлялося (або повідомлялося після їх виправлення, як я вже про них знав :)).

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 (відповідайте, редагуючи свою відповідь , а не тут у коментарях)?
Пітер Мортенсен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.