Подтвердить что ты не робот

Великая, единая теория обработки ошибок PHP

aka, Поиск универсального обработчика ошибок (ΟΚ для коммерческого использования)

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

Чтобы быть объективным, вот что мне кажется нужным. Пожалуйста, поправьте меня, если я ошибаюсь, и укажите мне хороший код, если вы согласитесь.

  • Я хочу записать как можно больше информации - не зная, что это за ошибка.

  • так, например, имеет смысл сбросить стек вызовов.

  • и $_GET, $_POST и $_SESSION.

  • и я хочу, чтобы стек вызовов и глобалы были довольно напечатаны

  • Я хочу, чтобы какой-то "простой текст", а не CSS и причудливый JS, расширял/сворачивал информацию. Моим пользователям, возможно, придется вырезать/вставлять в электронную почту или даже распечатывать и отправлять по факсу.

  • Я хотел бы иметь возможность добавлять заголовок моего собственного проекта, желательно как параметр, но при необходимости я могу взломать код. Заголовок может включать в себя версию программы, временную метку и т.д. (И, в моем случае, у меня есть контрольный трек, поэтому я могу включить в действие последние действия пользователя, что привело к сбою).

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

4b9b3361

Ответ 1

Я предлагаю пойти "Исключения".

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

function exception_error_handler($errno, $errstr, $errfile, $errline ) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}
set_error_handler("exception_error_handler");

Хотя это поведение лучше всего работает в среде ООП. Если у вас нет единственной точки входа (например, frontcontroller), вы можете также исключить исключения из этого:

function myException($exception)
{
    echo "<b>Exception:</b> " , $exception->getMessage();
}

set_exception_handler('myException');

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

function parseException($e) {
    $result = 'Exception: "';
    $result .= $e->getMessage();
    $trace = $e->getTrace();
    foreach (range(0, 10) as $i) {
        $result .= '" @ ';
        if (!isset($trace[$i])) {
            break;
        }
        if (isset($trace[$i]['class'])) {
            $result .= $trace[$i]['class'];
            $result .= '->';
        }
        $result .= $trace[$i]['function'];
        $result .= '(); ';
        $result .= $e->getFile() . ':' . $e->getLine() . "\n\n";
    }

    return $result;
}

Оттуда оценка глобальных и т.д. - это прогулка по парку. Вы можете найти вдохновение для панели инструментов Symfony Framework Debug, которая предлагает многие из этих запросов.

Ответ 2

Я не могу поверить, что это еще не было предложено.

В моей компании мы просто используем Exceptions с помощью специального обработчика ошибок. Обработчик ошибок скомпилирует отладочное сообщение с помощью:

  • GET, POST, COOKIES, SESSION, GLOBALS
  • След
  • Сообщение об ошибке (либо сообщение в Exception, либо предупреждение, да, вы также должны улавливать предупреждения, даже STRICTness).

Затем сообщение затем отправляется на сервер мониторинга, если это не удается, он попытается отправить нам адрес электронной почты, который не работает, он попытается log to database (и если это не удастся, он будет записываться в файл). Если ошибка является "фатальной" ошибкой в ​​том смысле, что ваш вывод не может быть гарантирован, вы можете выбрать букву 500 и распечатать сообщение "oops" по умолчанию.

Я бы посоветовал всегда автоматически сообщать обо всех ошибках. Единственные ошибки, о которых вы не хотите знать, - это ошибки, вызванные ошибочным вводом пользователем. В этом случае ошибки должны быть представлены пользователю как-то. Я обнаружил, что для каждого исключения вы можете определить, является ли это ошибкой в ​​вашей системе, или ошибка пользователя. Например: ссылка на страницу, а затем удаление страницы (это вызовет 404). Вы не хотите знать о 404, но ваш клиент делает.

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

Дополнительно. Есть трюк, который вы можете использовать, чтобы поймать pesky Fatal Errors. Вы можете использовать register_shutdown_handler для регистрации функции, которая всегда будет работать после завершения вашего PHP скрипт. Затем вы можете использовать error_get_last для проверки фатальной ошибки. Затем вы можете повторить описанные выше шаги, чтобы сообщить об ошибке. Это работает, я использую его все время.

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

Ответ 3

Предоставление всей вашей обработки ошибок конечному пользователю не является хорошей идеей.

1) он раскрывает внутреннюю структуру вашего кода - ОК, поэтому он должен быть защищен, даже если потенциальный злоумышленник имеет полный исходный код, - но нет смысла облегчать их жизнь.

2) Вы действительно считаете, что конечный пользователь будет добросовестно скопировать всю информацию и отправить ее вам?

3) вы подавляете пользователя с большим количеством информации, о которой они не заботятся.

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

Помните, что с PHP все данные очищаются, когда завершается script. Если вы пишете приложение Java или программу на C, то вы действительно не хотите постоянно накапливать данные - поэтому единственными параметрами являются запись записей отладки/информации в журнал, а затем, когда возникает ошибка, пишите трассировку стека к журналу. Но с PHP для веб-страниц я обычно храню журнал этих данных в переменной PHP, а затем записываю его в файл , когда возникает ошибка, а также трассировка стека (или когда завершается script и я установил флаг в коде, например, через сеанс, для анализа того, как он ведет себя в сценариях без ошибок).

Сколько записей вы записываете - я обычно регистрирую все точки входа и выхода fn и любые SQL-запросы.

например.

function logit($msg)
{
   global $runlog;
   static $basetime;
   if (!$basetime) $basetime=time();
   $runlog.="+" . time()-$basetime . "s : " . $msg . "\n";
}