В РНР версии 5 существуют два метода перехвата ошибок во время выполнения программы:
● регистрация обработчика ошибки.
● исключений;
РНР поддерживает средства, позволяющие "перехватывать" момент возникновения той или иной ошибки (или предупреждения) и вызывать при этом функцию, написанную пользователем.
Метод перехвата ошибок при помощи пользовательских функций далек от совершенства. Сделать с сообщением что-либо разумное, кроме как записать его куда-нибудь и завершить программу (при необходимости), не представляется возможным.
Метод исключений, который рассмотрен в п. 3, лишен этого недостатка, он достаточно сложен и практически не реализован на уровне стандартных функций РНР.
Пример использования обработчика ошибок приведен в листинге 2.1.
Листинг 2.1. Файл handler1.php
<?php ## Перехват ошибок и предупреждений.
// Определяем новую функцию-обработчик.
function myErrorHandler($errno, $msg, $file, $line) {
// Если используется @, ничего не делать.
if (error_reporting() == 0) return;
// Иначе - выводим сообщение.
echo '<div style="border-style:inset; border-width:2">';
echo "Произошла ошибка с кодом <b>$errno</b>!<br>";
echo "Файл: <tt>$file</tt>, строка $line.<br>";
echo "Текст ошибки: <i>$msg</i>";
echo "</div>";
}
// Регистрируем ее для всех типов ошибок.
set_error_handler("myErrorHandler", E_ALL);
// Вызываем функцию для несуществующего файла, чтобы
// сгенерировать предупреждение, которое будет перехвачено.
filemtime("spoon");
?>
Что бы ни произошло, программа всегда продолжает свою работу — просто в момент возникновения ошибочной ситуации вызывается функция-обработчик, а затем выполнение идет дальше. Именно по этой причине обработчик нельзя назвать кодом восстановления.
2.1 ФУНКЦИЯ set_error_handler
Функция
string set_error_handler(string $runcName [, int $errorTypes])
регистрирует пользовательский обработчик ошибок — функцию, которая будет вызвана при возникновении сообщений, указанных в $errorTypes типов (битовая маска, например, E_ALL ~ E_NOTICE).
Сообщения, не соответствующие маске $errorTypes, будут в любом случае обрабатываться встроенными средствами РНР, а не предыдущей установленной функцией-перехватчиком. Имя пользовательской функции передается в параметре $runcName. Если до этого был установлен какой-то другой обработчик, функция вернет его имя — с тем, чтобы его можно было позже восстановить. Пользовательский обработчик должен задаваться так, как показано в листинге 2.2.
Листинг 2.2. Файл handler0.php
<?php ## Перехват ошибок и предупреждений.
// Определяем новую функцию-обработчик.
function myErrorHandler($errno, $msg, $file, $line) {
echo '<div style="border-style:inset; border-width:2">';
echo "Произошла ошибка с кодом <b>$errno</b>!<br>";
echo "Файл: <tt>$file</tt>, строка $line.<br>";
echo "Текст ошибки: <i>$msg</i>";
echo "</div>";
}
// Регистрируем ее для всех типов ошибок.
set_error_handler("myErrorHandler", E_ALL);
// Вызываем функцию для несуществующего файла, чтобы
// сгенерировать предупреждение, которое будет перехвачено.
filemtime("spoon");
?>
Теперь при возникновении любой ошибки или даже предупреждения в программе будет вызвана функция myErrorHandier(). Ее аргументы получат значения, соответственно, номера ошибки, текста ошибки, имени файла и номера строки, в которой было сгенерировано сообщение.
К сожалению, не все типы ошибок могут быть перехвачены таким образом. Например, ошибки трансляции во внутреннее представление E_PARSE, а также E_ERROR немедленно завершают работу программы. Вызовы функции die()также не перехватываются.
Назначить функцию реакции на E_PARSE и E_ERROR все же можно. Дело в том, что перехватчик выходного потока скрипта, устанавливаемый функцией ob__start(), обязательно вызывается при завершении работы программы, в том числе в случае фатальной ошибки. Конечно, ему не передается сообщение об ошибке и ее код; он должен сам получить эти данные из "хвоста" выходного потока (например, используя функции для работы с регулярными выражениями).
В случае если пользовательский обработчик возвращает значение false (и только его!), считается, что ошибка не была обработана, и управление передается стандартному обработчику РНР (обычно он выводит текст ошибки в браузер). Все остальные возвращаемые значения (включая даже null или, что то же самое, в случае, если оператора return вообще нет), приводят к подавлению запуска стандартной процедуры обработки ошибок.
2.2 ФУНКЦИЯ restore_error_handler()
void restore_error_handler()
Когда вызывается функция set_error_handler(), предыдущее имя пользовательской функции запоминается в специальном внутреннем стеке РНР. Чтобы извлечь это имя и тут же его установить в качестве обработчика, применяется функция restore_error_handler().Пример:
// Регистрируем обработчик для всех типов ошибок.
set_error_handler("myErrorHandler", E_ALL);
// Включаем подозрительный файл.
include "suspicious_file.php";
// Восстанавливаем предыдущий обработчик.
restore_error_handler();
Необходимо следить, чтобы количество вызовов restore_error_handler() было в точности равно числу вызовов set_error_handler().Нельзя восстановить то, чего нет.
2.3 ПРОБЛЕМЫ С ОПЕРАТОРОМ @
Пользовательская функция перехвата ошибок вызывается вне зависимости от того, был ли использован оператор подавления ошибок в момент генерации предупреждения. Это очень неудобно: поставив оператор @ перед вызовом filemtime(), мы увидим, что результат работы скрипта нисколько не изменился: текст предупреждения по-прежнему выводится в браузер.
Для того чтобы решить проблему, воспользуемся полезным свойством оператора @: на время своего выполнения он устанавливает error_reporting, равным нулю. Значит, мы можем определить, был ли вызван оператор @ в момент "срабатывания" обработчика, по нулевому значению функции error_reporting() (при вызове без параметров она возвращает текущий уровень ошибок) — листинг 2.3.
Листинг 2.3. Файл handler.php
<?php ## Перехват ошибок и предупреждений.
// Определяем новую функцию-обработчик.
function myErrorHandler($errno, $msg, $file, $line) {
// Если используется @, ничего не делать.
if (error_reporting() == 0) return;
// Иначе - выводим сообщение.
echo '<div style="border-style:inset; border-width:2">';
echo "Произошла ошибка с кодом <b>$errno</b>!<br>";
echo "Файл: <tt>$file</tt>, строка $line.<br>";
echo "Текст ошибки: <i>$msg</i>";
echo "</div>";
}
// Регистрируем ее для всех типов ошибок.
set_error_handler("myErrorHandler", E_ALL);
// Вызываем функцию для несуществующего файла, чтобы
// сгенерировать предупреждение, которое будет перехвачено.
@filemtime("spoon");
?>
Теперь все работает корректно: предупреждение не отображается в браузере, т. к. применен оператор @.
ГЕНЕРАЦИЯ ОШИБОК
Функция
void trigger_error(string $message [, int $type])
предназначена для искусственной генерации сообщения об ошибки с указанным типом. В РНР существует несколько констант, чьи имена начинаются с E_USER_, которые можно использовать наравне с E_ERROR, E_WARNING и т. д., но только для персональных нужд. Именно с функцией trigger_error() они чаще всего и применяются. Вот эти константы:
E_ERROR
E_WARNING
E_USER_NOTICE
Как их использовать — целиком зависит от программиста: никаких ограничений не налагается.
int error_log(string $msg [,int $type=O] [,string $dest] [, string $extra_headers])
Выше мы рассматривали директивы log_errors и error_log, которые заставляют РНР записывать диагностические сообщения в файл, а не только выводить их в браузер. Функция error_log по своему смыслу похожа на trigger_error(), однако, она заставляет интерпретатор записать некоторый текст ($msg) в файл журнала (при нулевом $type и по умолчанию), заданный в директиве error_log. Основные значения аргумента $type, которые может принимать функция, перечислены ниже:
● $type = = 0
Записывает сообщение в системный файл журнала или в файл, заданный в директиве error_log.
● $type = = 1
Отправляет сообщение по почте адресату, чей адрес указан в $dest. При этом $extra_headers используется в качестве дополнительных почтовых заголовков.
● $type == 3
Сообщение добавляется в конец файла с именем $dest.
СТЕК ВЫЗОВОВ ФУНКЦИЙ
В РНР версии 4.3.0 и старше существует возможность проследить всю цепочку вызовов функций, начиная от главной программы и заканчивая текущей выполняемой процедурой.
Функция
list debug_backtrace()
возвращает большой список, в котором содержится информация о "родительских" функциях и их аргументах. Результат работы листинга 2.4 говорит сам за себя.
Листинг2.4. Файл trace.php
<?php ## Вывод дерева вызовов функции.
function inner($a) {
// Внутренняя функция.
echo "<pre>"; print_r(debug_backtrace()); echo "</pre>";
}
function outer($x) {
// Родительская функция.
inner($x*$x);
}
// Главная программа.
outer(3);
?>
После запуска этого скрипта будет напечатан примерно следующий результат (его чуть сжали):
Array (
[0] => Array (
[file] => z:\home\book\original\src\interpreter\trace.php
[line] => 6
[function] => inner
[args] => Array ([0] => 9)
)
[1] => Array (
[file] => z:\home\book\original\src\interpreter\trace.php
[line] => 8
[function] => outer
[args] => Array ([0] => 3)
)
)
Как видите, в массиве оказалась все информация о промежуточных вызовах функций.
Функцию удобно применять, например, в пользовательском обработчике ошибок. Тогда можно сразу же определить, в каком месте было сгенерировано сообщение и какие вызовы к этому привели, В крупных программах уровень вложенности функций может достигать нескольких десятков, поэтому, наверное, вместо print_r() стоит написать свой собственный код для вывода дерева вызовов в более компактной форме.
Дата: 2019-05-28, просмотров: 202.