Как мне отловить фатальную ошибку PHP (`E_ERROR`)?

Я могу использовать set_error_handler() для обнаружения большинства ошибок PHP, но он не работает для фатальных (E_ERROR) ошибок, таких как вызов несуществующей функции. Есть ли другой способ отловить эти ошибки?

Я пытаюсь вызвать в mail() по поводу всех ошибок и использую PHP 5.2.3.

Смотрите также:stackoverflow.com/questions/1087365/…
dreftymac 15.03.2018 23:42
Смотрите также:bugs.php.net/bug.php?id=41418
dreftymac 26.07.2018 20:14
Смотрите также:stackoverflow.com/questions/7116995
dreftymac 13.11.2018 00:54

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

DigitalJedi805 01.12.2014 22:36
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
567
4
377 545
17

Ответы 17

Не совсем. Фатальные ошибки называются так, потому что они фатальны. От них не оправиться.

ловля и восстановление - две очень разные вещи.

Simon Forsberg 02.07.2012 00:40

PHP не предоставляет обычных средств для обнаружения фатальных ошибок и восстановления после них. Это связано с тем, что обработка обычно не должна восстанавливаться после фатальной ошибки. Строка, соответствующая выходному буферу (как было предложено в исходной публикации, методика, описанная на PHP.net), определенно не рекомендуется. Это просто ненадежно.

Вызов функции mail () из метода обработчика ошибок тоже оказался проблематичным. Если бы у вас было много ошибок, ваш почтовый сервер был бы загружен работой, и вы могли бы оказаться с корявым почтовым ящиком. Чтобы избежать этого, вы можете рассмотреть возможность запуска cron для периодического сканирования журналов ошибок и соответствующей отправки уведомлений. Вы также можете изучить программное обеспечение для мониторинга системы, такое как Nagios.


Чтобы поговорить с битом о регистрации функции выключения:

Это правда, что вы можете зарегистрировать функцию выключения, и это хороший ответ.

Дело в том, что обычно мы не должны пытаться восстанавливаться после фатальных ошибок, особенно не используя регулярное выражение для выходного буфера. Я отвечал на принятый ответ, который связан с предложением на php.net, которое с тех пор было изменено или удалено.

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

Возможно, стоит отметить, что более поздние версии PHP (около 5.1), похоже, вызывают функцию выключения раньше, до того, как вызывается обратный вызов буферизации вывода. В версии 5 и ранее этот порядок был обратным (за обратным вызовом буферизации вывода следовала функция выключения). Кроме того, начиная с версии 5.0.5 (что намного раньше, чем версия 5.2.3 опрашивающего), объекты выгружаются задолго до того, как вызывается зарегистрированная функция выключения, поэтому вы не сможете полагаться на свои объекты в памяти для выполнения много чего.

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

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

Пффф, я помню те 650 000+ писем, которые получил на следующее утро. С тех пор мой ErrorHandler ограничен 100 электронными письмами на каждый веб-сервер.

Bob Fanger 23.09.2009 12:12

Это не правда. Вы можете зафиксировать фатальные ошибки с помощью register_shutdown_function.

hipertracker 26.09.2010 00:24

Существуют варианты использования для обнаружения фатальных ошибок. Например, наборы тестов не должны просто останавливаться при сбое одного из них, они должны сообщать о фатальной ошибке и переходить к следующему тесту. PHP просто делает слишком много «фатальных» ошибок.

Chad 20.04.2011 00:46

Да, говорить, что их «не следует ловить», очень недальновидно. В производственной системе вы необходимость, чтобы знать, когда что-то выходит из строя (настраивайте электронную почту или регистрируйте события в базе данных - обработка ошибок php по умолчанию не очень сложна).

B T 10.05.2011 01:28

Да, их «не надо ловить», но, к сожалению, ошибки часто используются там, где должны быть исключения (некачественные библиотеки, некачественные проекты). Это меня огорчает.

Keyo 11.07.2011 03:59

Я хочу вкратце прокомментировать то, что вы все говорите о том, что «ошибки нужно вылавливать, чтобы мы могли их исправить» ... Директивы ini ini log_errors и error_log.

Kelly Elton 27.10.2011 02:26

В дополнение к этому, я очень РЕДКО получал фатальную ошибку. Я имею в виду, что вы можете вызвать функцию или переменную, которых не существует, и они все еще продолжают работать. Вот почему так важно протестировать свой код до того, как он попадет в рабочую среду.

Kelly Elton 27.10.2011 02:30

Вы также можете добавить произвольное число в заголовок, например, 400 (например, это число не лучший выбор). Тогда в вашем журнале есть ошибка, и apache выдаст правильную страницу с ошибкой в ​​зависимости от номера.

Kelly Elton 27.10.2011 02:36

Вы также можете проверить это code.google.com/p/lagger, вы даже можете получить расширения, скажем, для Chrome, чтобы вы могли просматривать свой журнал ctrl + i и видеть ошибки и т. Таким образом, они не отображаются на странице, но вы все равно можете получить к ним доступ, не копаясь в файлах журналов.

Kelly Elton 27.10.2011 02:39

Это не правильно. Выявление фатальных ошибок совершенно нормально и правильно, поэтому вы можете делать такие вещи, как регистрировать их и выполнять очистку. Обработка ошибок PHP просто плохо спроектирована.

Glenn Maynard 17.05.2012 22:03

Запуская php 5.3.3-7 + squeeze13, я не согласен с тем, что «объекты выгружаются задолго до вызова зарегистрированной функции выключения». Я получил доступ к своим объектам в зарегистрированной функции выключения, и она работала нормально, все объекты имеют те же данные, что и до ошибки.

Simon Forsberg 02.07.2012 00:54

Любой другой язык JavaScript, C# и т. д. Позволяет ловить фатальные ошибки с помощью try / catch. PHP здесь просто неправильный.

Matthew Lock 18.09.2013 14:12

Некоторые фатальные ошибки не следует обнаруживать, но подавляющее большинство фатальных ошибок php должно быть исключением! Вызов метода для нулевой переменной должен быть исключением! Неподдерживаемые типы операндов должны быть исключением! PHP в основном вынуждает вас выполнять LBYL-программирование в тех случаях, когда это несовместимо.

Jeff Whiting 12.12.2013 23:07

Лично я считаю, что ВСЕ ошибки должны быть отловлены. Почему? Система №1 будет работать перед пользователем, поэтому вы можете занести все ошибки в базу данных и / или отправить электронное письмо разработчику. Ошибки, которые регистрируются в простом текстовом файле, не содержат почти бесполезной информации, никакой информации о том, какой пользователь их вызвал, или о том, как был вызван php-скрипт. # 2 В зависимости от того, как он был вызван (например, ajax или прямой html), вы можете захотеть вывести другой формат (json / xml), чем всегда HTML.

Rahly 11.05.2014 07:23

Верно. Дело в том, что ошибки следует отлавливать, но не следует отлавливать фатальные ошибки, чтобы как-то исправить, используя нечетные и ненадежные приемы (например, применение регулярного выражения к выходному буферу).

keparo 01.07.2015 04:15

Регистрируйте фатальные ошибки с помощью 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.

Смотрите также:

+1 Это правильный ответ действительный. Я не знаю, почему люди зацикливаются на том, что «вы не можете восстановиться после фатальных ошибок» - в вопросе ничего не говорилось о восстановлении.

David Harkness 02.11.2011 01:53

Спасибо, хорошо. Восстановление после фатальных ошибок (например, ограничения памяти) - это не то, что я бы пытался сделать, но возможность обнаружения этих ошибок (без отправки клиентом заявки в службу поддержки) имеет решающее значение.

Ilija 11.02.2012 14:51

он также перехватывает ошибки синтаксического анализа, если они не находятся в том же файле объявления функции регистра. (при условии, что это объявление в основном файле)

Luca Rainone 21.08.2012 16:37

Обратите внимание, что функция обратного вызова должна выполняться до вызова register_shutdown_function ().

taco 24.09.2012 19:23

@SlavaN переменные устанавливаются перед if, поэтому они просто отправят электронное письмо о том, что ошибка неизвестна, если ошибка не может быть установлена.

Scott Nicol 07.06.2013 15:27

Используя обычную почту: mail("[email protected]", "My Site: FATAL ERROR", "Details: " . $errno . ' ' . $errstr . ' ' . $errfile . ' ' . $errline);

Eric Muyser 17.06.2013 11:02

@ScottNicol Slava V верен, потому что функция выключения вызывается каждый раз, когда скрипт завершает работу. Теперь, когда код написан, электронное письмо будет отправляться при КАЖДОЙ загрузке страницы.

Nate 21.01.2014 07:46

@periklis (ниже) имеет лучший ответ

user26676 11.06.2014 17:46

Обратите внимание, что register_shutdown_function вызывается перед деструкторами, поэтому, если деструктор генерирует исключение, он не будет обработан вашим обратным вызовом.

Alain Tiemblo 23.10.2014 15:32

Незначительная придирка, но для краткости, поскольку вы ожидаете массив, вы можете просто сделать if ($error) вместо if ( $error !== NULL).

Mahn 09.11.2014 16:18

Обратите внимание, что (1) register_shutdown_function добавляет заданную функцию в очередь обратных вызовов завершения работы, поэтому она должна хорошо работать с неизвестным кодом, выполняемым до и после, во время одного и того же выключения; (2) он не проверяет, находится ли данная функция уже в очереди или нет; (3) отменить регистрацию зарегистрированной функции невозможно. Следовательно, зарегистрированная функция выключения не только должна хорошо взаимодействовать с другим неизвестным кодом, но и с возможными дополнительными исполнениями своего собственного кода. Совет: используйте что-нибудь вроде static $count; ... $count++; ... if ($count > 1) return; ... (fatal handling) .

aercolino 15.01.2015 23:50

РЖУ НЕ МОГУ ! 480 голосов за? для чего? приведенный вами пример: `$ trace = print_r (debug_backtrace (false), true);` может и заблокирует серверный скрипт! это случилось со мной. Да да, не обязательно. но в конце концов так и будет. Поскольку этот цикл не ограничен, и вы не знаете, что делаете. Избегайте этого любой ценой, если вам не нравится перезапускать сервер Apache.

Toskan 03.06.2016 01:22

Поскольку error_mail находится внутри оператора if, значения переменных по умолчанию никогда не используются. Можно смело снимать ...

Stanimir Stoyanov 29.06.2017 16:25

Примечание: это не 100% правильный ответ. Любое место, где для игнорирования ошибок используется символ @, по-прежнему будет УСТАНОВИТЬ последнюю ошибку (чтобы вы могли обрабатывать ошибки). Таким образом, ваш сценарий завершается без проблем, но функция register_shutdown_function по-прежнему считает, что произошла ошибка. Только с PHP 7 появилась функция error_clear_last ().

Rahly 25.08.2017 02:56

debug_backtrace показывает только стек «fatal_handler», но не стек, когда произошла ошибка. Интересно, есть ли способ увидеть стек, когда произошла фатальная ошибка.

user34814 17.03.2020 11:44

Спасибо, это помогло мне :)

Soheyl 14.05.2020 00:10

Я только что придумал это решение (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');

Различные типы ошибок определены в Предопределенные константы.

Это решение делает для меня гораздо больше, чем самый популярный ответ. Ответ с наивысшим рейтингом будет отправлять вам электронное письмо при каждом запуске скрипта, даже если ошибки нет. Этот строго работает с фатальной ошибкой.

kmoney12 26.03.2013 05:04

@periklis, если последняя ошибка уже была обработана, error_get_last все равно вернет ее, не так ли?

Pacerier 12.07.2013 16:49

@Pacerier Я не уверен, что вы имеете в виду под словом «обработано», поскольку ошибки не являются исключением, но я полагаю, что ответ - «да».

periklis 12.07.2013 17:10

@periklis, я имею в виду, что вы уже "обработали" предыдущую ошибку с помощью обработчика ошибок в set_error_handler. В этом случае вы снова будете «обрабатывать» эту ошибку, когда скрипт завершит работу.

Pacerier 12.07.2013 17:19

@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 12.07.2013 17:38

@periklis, он не был точным. В set_error_handler иногда мы заставляем наш обработчик возвращать false, чтобы продолжить поведение по умолчанию (нормальная обработка ошибок). В этом случае error_get_last снова облажается, сообщив о той же ошибке. Какое решение лучше?

Pacerier 12.07.2013 18:36

Если вы ссылаетесь на FATAL ошибки как на исходный вопрос OP, то ваш set_error_handler() не будет выполнен. set_error_handler() может обрабатывать определенные типы ошибок. Я бы посоветовал вам использовать register_shutdown_function() для всего остального (полный список см. В руководстве).

periklis 13.07.2013 09:13

Возможно, это очевидно, вызов register_shutdown_function() должен быть раньше, чем возникнет фатальная ошибка. use_1T_memory(); /* memory exhausted error here! */ register_shutdown_function('shutDownFunction'); не будет работать должным образом.

Nobu 26.03.2016 02:32

В дополнение к E_ERROR вы, вероятно, захотите проверить E_COMPILE_ERROR, E_CORE_ERROR и, возможно, даже E_PARSE. Как говорили другие, любая функция, зарегистрированная с помощью register_shutdown_function (), будет запускаться при каждом вызове скрипта, поэтому вы захотите свести к минимуму любой код, который вы вставляете в такую ​​функцию, и не делать такие вещи, как попытки отправить заголовки без тестирования с headers_sent () или что-нибудь еще, что может вызвать дополнительные ошибки.

Night Owl 16.05.2016 08:33

если ошибок не было, код: if ($ error ['type'] === E_ERROR) вызовет предупреждение, потому что вы пытаетесь получить доступ к несуществующему ключу в NULL

Jimmmy 20.01.2017 11:41

Что делать, если внутри функции выключения возникает еще одна фатальная ошибка? Будет ли код входить в бесконечный цикл?

HomeIsWhereThePcIs 16.07.2020 12:48

Вы не можете создать исключение внутри зарегистрированной функции выключения, например:

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

Что ж, фатальные ошибки вроде бы можно отловить и другим способом :)

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

Я бы дал 10 голосов, если бы мог. Он отлично работает для меня с теми странными ошибками, которые иногда возникают, когда страница бомбится и ничего не регистрируется. Я бы не стал использовать в реальном производственном коде, но здорово добавить на страницу, когда требуется быстрый ответ на то, что не удается. Спасибо!

Night Owl 14.01.2012 09:35

Одно из лучших решений, которые я нашел в Интернете. Работает как шарм.

Bounce 12.02.2013 02:56

Каким образом? Объяснение было бы в порядке, особенно если это одно из лучших решений в Интернете (может стать еще лучше).

Peter Mortensen 21.04.2020 04:33

Например, весь необходимый CSS-контент? Нельзя ли сократить до самого необходимого? Ответьте, отредактировав свой ответ, а не здесь, в комментариях (при необходимости).

Peter Mortensen 21.04.2020 04:35

@PeterMortensen Я не претендую на лучшее. Также это мое личное решение проблемы, есть и другие варианты получше, более профессиональные. Как кто-то предположил, он не подходит для производства. Css есть bcz Я только что вырезал свой личный код

sakhunzai 22.04.2020 12:03

@PeterMortensen также нет единого решения этой проблемы, возможно, он получил голоса за bcz, он решает проблему, с которой они сталкиваются, а не bcz в лучшем виде

sakhunzai 22.04.2020 12:06

Мне нужно обрабатывать фатальные ошибки для производства, чтобы вместо этого отображать вывод HTML в статическом стиле 503 Служба Недоступна. Это, безусловно, разумный подход к «отлову фатальных ошибок». Вот что я сделал:

У меня есть специальная функция обработки ошибок error_handler, которая будет отображать мою HTML-страницу «503 service unavailable» на любой 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 о «фатальной ошибке».

Обратите внимание на строгую проверку isset () и функции отключения звука @, поскольку мы не хотим, чтобы наши сценарии error_handler генерировали какие-либо ошибки.

По-прежнему соглашаясь с keparo, обнаружение фатальных ошибок лишает вас цели «FATAL error», поэтому на самом деле это не предназначено для дальнейшей обработки. Не запускайте никаких функций mail () в этом процессе завершения работы, так как вы обязательно создадите резервную копию почтового сервера или своего почтового ящика. Лучше записать эти вхождения в файл и запланировать задание cron, чтобы найти эти файлы журнал ошибок и отправить их администраторам.

Я разработал эту функцию, чтобы сделать возможным "песочницу" кода, который может вызвать фатальную ошибку. Поскольку исключения, вызванные закрытием register_shutdown_function, не генерируются из стека вызовов до фатальной ошибки, я вынужден выйти после этой функции, чтобы обеспечить единообразный способ ее использования.

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

PHP имеет обнаруживаемые фатальные ошибки. Они определены как E_RECOVERABLE_ERROR. Руководство по PHP описывает E_RECOVERABLE_ERROR как:

Catchable fatal error. It indicates that a probably dangerous error occured, but did not leave the Engine in an unstable state. If the error is not caught by a user defined handle (see also set_error_handler()), the application aborts as it was an E_ERROR.

Вы можете «поймать» эти «фатальные» ошибки, используя set_error_handler () и проверяя E_RECOVERABLE_ERROR. Я считаю полезным генерировать исключение при обнаружении этой ошибки, тогда вы можете использовать try / catch.

Этот вопрос и ответ представляют собой полезный пример: Как я могу отловить «обнаруживаемую фатальную ошибку» при хинтинге типа PHP?

Однако ошибки E_ERROR можно обработать, но нельзя исправить, поскольку механизм находится в нестабильном состоянии.

Я разработал способ перехвата всех типов ошибок в 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();
?>

Что делает строка @include 'content.php'?

Marco 20.11.2017 21:50

Если вы используете 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
    }
?>

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

Я реализовал ловушку 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"? Вы имеете в виду Отправить письмо (ответ от редактирование вашего ответа, а не здесь в комментариях)?

Peter Mortensen 21.04.2020 04:52

Вот просто хороший трюк, чтобы получить текущий метод 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 перестает отображать ошибку. В противном случае текст ошибки будет отправлен клиенту до вашего обработчика ошибок.

Проголосовал за это из-за строки ini_set ('display_errors', false);

Sahib Khan 24.11.2016 10:59

Если по какой-то причине этот бит включен, он все равно будет отображать ошибки php, даже если вы обрабатываете его по-другому

Sahib Khan 24.11.2016 11:00

Хорошее решение, найденное в 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


Возможно, лучшее решение - от Монолог:

Link to the full class code:
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;
    }

    // [...]
}

Поскольку большинство ответов здесь излишне многословны, вот моя не уродливая версия ответа, получившего наибольшее количество голосов:

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

Вы не можете поймать / обработать фатальные ошибки, но вы можете зарегистрировать / сообщить о них. Для быстрой отладки я изменил один ответ на этот простой код

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 первая строка. В основном входной файл вашего скрипта / программы, поэтому он выполняется первым, если это невозможно, поместите его в общий файл

zainengineer 31.03.2020 14:05

Фатальные ошибки или исправимые фатальные ошибки теперь вызывают экземпляры 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

Есть идеи о том, как использовать это, чтобы поймать ошибку, такую ​​как Fatal error: Trait 'FailedTrait' not found in, при использовании ReflectionClass?

TCB13 20.04.2018 14:41

@ TCB13 пытается обернуть внутреннее содержимое try в файл и include "filename.php" вместо этого в блок try, тогда блок захвата Throwable, по крайней мере, работает для ParseError.

Niloct 01.11.2018 21:09

Другие вопросы по теме