Смекни!
smekni.com

Обработка ошибок в коде программ РНР (стр. 8 из 8)

$args = func_get_args();

call_user_func_array($this->prevHdl, $args);

} else {

// Иначе возвращаем false, что вызывает запуск встроенного

// обработчика PHP.

returnfalse;

}

}

// Возвращаем true (все сделано).

returntrue;

}

// Получаем текстовое представление типа ошибки.

$types = array(

"E_ERROR", "E_WARNING", "E_PARSE", "E_NOTICE", "E_CORE_ERROR",

"E_CORE_WARNING", "E_COMPILE_ERROR", "E_COMPILE_WARNING",

"E_USER_ERROR", "E_USER_WARNING", "E_USER_NOTICE", "E_STRICT",

);

// Формируем имя класса-исключения в зависимости от типа ошибки.

$className = __CLASS__ . "_" . "Exception";

foreach ($types as $t) {

$e = constant($t);

if ($errno & $e) {

$className = $t;

break;

}

}

// Генерируем исключение нужного типа.

throw new $className($errno, $errstr, $errfile, $errline);

}

}

/**

* Базовый класс для всех исключений, полученных в результате ошибки PHP.

*/

abstract class PHP_Exceptionizer_Exception extends Exception {

public function __construct($no=0, $str=null, $file=null, $line=0) {

parent::__construct($str, $no);

$this->file = $file;

$this->line = $line;

}

}

/**

* Создаем иерархию "серьезности" ошибок, чтобы можно было

* ловить не только исключения с указанием точного типа, но

* и сообщения, не менее "фатальные", чем указано.

*/

class E_EXCEPTION extends PHP_Exceptionizer_Exception {}

class AboveE_STRICT extends E_EXCEPTION {}

class E_STRICT extends AboveE_STRICT {}

class AboveE_NOTICE extends AboveE_STRICT {}

class E_NOTICE extends AboveE_NOTICE {}

class AboveE_WARNING extends AboveE_NOTICE {}

class E_WARNING extends AboveE_WARNING {}

class AboveE_PARSE extends AboveE_WARNING {}

class E_PARSE extends AboveE_PARSE {}

class AboveE_ERROR extends AboveE_PARSE {}

class E_ERROR extends AboveE_ERROR {}

class E_CORE_ERROR extends AboveE_ERROR {}

class E_CORE_WARNING extends AboveE_ERROR {}

class E_COMPILE_ERROR extends AboveE_ERROR {}

class E_COMPILE_WARNING extends AboveE_ERROR {}

class AboveE_USER_NOTICE extends E_EXCEPTION {}

class E_USER_NOTICE extends AboveE_USER_NOTICE {}

class AboveE_USER_WARNING extends AboveE_USER_NOTICE {}

class E_USER_WARNING extends AboveE_USER_WARNING {}

class AboveE_USER_ERROR extends AboveE_USER_WARNING {}

class E_USER_ERROR extends AboveE_USER_ERROR {}

// Иерархии пользовательских и встроенных ошибок не сравнимы,

// т.к. они используются для разных целей, и оценить

// "серьезность" нельзя.

?>

Перечислим достоинства описанного подхода.

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

● Используется удобный синтаксис обработки исключений, гораздо более "прозрачный", чем работа с set_error_handler(). Каждый объект-исключение дополнительно содержит информацию о месте возникновения ошибки, а также сведения о стеке вызовов функций; все эти данные можно извлечь с помощью соответствующих методов класса Exception.

● Можно перехватывать ошибки выборочно, по типам, например, отдельно обрабатывать сообщения E_WARNING и отдельно — E_NOTICE.

● Возможна установка "преобразователя" не для всех разновидностей ошибок, а только для некоторых из них (например, превращать ошибки E_WARNING в исключения класса E_WARNING, но "ничего не делать" с E_NOTICE).

● Классы-исключения объединены в иерархию наследования, что позволяет при необходимости перехватывать не только ошибки, точно совпадающие с указанным типом, но также заодно и более "серьезные".

3.10.4 Иерархия исключений

Остановимся на последнем пункте приведенного выше списка. Взглянув еще раз в конец листинга 3.12, вы можете обнаружить, что классы-исключения объединены в довольно сложную иерархию наследования. Главной "изюминкой" метода является введение еще одной группы классов, имена которых имеют префикс Above. При этом более "серьезные" Above-классы ошибок являются потомками всех "менее серьезных". Например, AboveERROR, самая "серьезная" из ошибок, имеет в "предках" все остальные Above-классы, a AboveE_STRICT, самая слабая, не наследует никаких других Above-классов. Подобная иерархия позволяет нам перехватывать ошибки не только с типом, в точности совпадающим с указанным, но также и более серьезные.

Например, нам может потребоваться перехватывать в программе все ошибки класса E_USER_WARNING и более фатальные E_USER_ERROR. Действительно, если мы заботимся о каких-то там предупреждениях, то уж конечно должны позаботиться и о серьезных ошибках. Мы могли бы поступить так:

try {

// генерацияошибки

} catch (E_USER_WARNING $e) {

// кодвосстановления

} catch (E_USER_ERROR $e) {

// точно такой же код восстановления — приходится дублировать

}

Сложная иерархия исключений позволяет нам записать тот же фрагмент проще и понятнее (листинг3.13).

Листинг 3.13. Файл w2e_hier.php

<?php ## Иерархия ошибок.

require_once "lib/config.php";

require_once "PHP/Exceptionizer.php";

suffer();

function suffer() {

$w2e = new PHP_Exceptionizer(E_ALL);

try {

// Генерируем ошибку.

trigger_error("Damn it!", E_USER_ERROR);

} catch (AboveE_USER_WARNING $e) {

// Перехват ошибок: E_USER_WARNING и более серьезных.

echo "<pre><b>Перехвачена ошибка!</b>&bsol;n", $e, "</pre>";

}

}

?>

3.10.5 Фильтрация по типам ошибок

Использование механизма обработки исключений подразумевает, что после возникновения ошибки "назад ходу нет": управление передается в catch-блок, а нормальный ход выполнения программы прерывается. Возможно, вы не захотите такого поведения для всех типов предупреждений РНР. Например, ошибки класса E_NOTICE иногда не имеет смысла преобразовывать в исключения и делать их, таким образом, излишне фатальными.

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

Вы можете указать в первом параметре конструктора PHP_Exceptionizer, какие типы ошибок необходимо перехватывать. По умолчанию там стоит E_ALL (т. е. перехватывать все ошибки и предупреждения), но вы можете задать и любое другое значение (например, битовую маску E_ALL~ E_NOTICE ~ E_STRICT), если пожелаете.

Существует еще и второй параметр конструктора. Он указывает, что нужно делать с сообщениями, тип которых не удовлетворяет битовой маске, приведенной в первом параметре. Можно их либо обрабатывать обычным способом, т. е. передавать ранее установленному обработчику (false), либо же попросту игнорировать (true).

Напомним, что в РНР 5 функция set_error_handler() принимает второй необязательный параметр, в котором можно указать битовую маску "срабатывания" обработчика. А именно для тех типов ошибок, которые "подходят" под маску, будет вызвана пользовательская функция, а для всех остальных— стандартная, встроенная в РНР. Класс PHP_Exceptionizer ведет себя несколько по-другому: в случае несовпадения типа ошибки с битовой маской будет вызван не встроенный в РНР обработчик, а предыдущий назначенный (если он имелся). Таким образом, реализуется стек перехватчиков ошибок. В ряде ситуаций это оказывается более удобным.

3.10.6 Перспективы

По неофициальным данным, в РНР версии 5.1 (и старше) разработчики планируют реализовать встроенный механизм преобразования ошибок в исключения. Для этого, предположительно, будет использоваться инструкция declare, позволяющая задавать блоку программы различные свойства (в том числе, что делать при возникновении ошибки). Код перехвата может выглядеть, например, так:

// Включаем "исключительное" поведение ошибок в РНР.

declare(exception_map='+standard:streams:*') {

try {

//В действительности генерируется исключение, а не предупреждение.

fopen("spoon", 'r');

} catch (Exception $e) {

if ($e->getCode() = = 'standard:streams:E_NOENT ') {

echo "Ложка не существует!";

}

}

}

// При выходе из declare-блока предыдущие свойства восстанавливаются.

К сожалению, в РНР версии 5.0 ничего подобного нет. Проверьте, возможно, данная функциональность появилась в вашей версии интерпретатора (см. документацию на инструкцию declare по адресу http://php.net/dedare).


ЗАКЛЮЧЕНИЕ

Рассмотрена одна из самых важных и популярных при программировании задач — обработка ошибок в коде программы. Уточнено понятие термина "ошибка" и определена его роль в программировании, а также приведены различные классификации ошибочных ситуаций. Представлено описание понятия "исключение" и способы использования конструкции try...catch, а также описаны некоторые особенности ее работы в РНР. Описан механизм наследования и классификации исключений, использование которого может сильно сократить код программы и сделать его универсальным. Представлен код библиотеки, позволяющей обрабатывать многочисленные ошибки и предупреждения, генерируемые функциями РНР, как обыкновенные исключения.

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


ЛИТЕРАТУРА

1. Скляр Д., Трахтенберг А. PHP. Сборник рецептов. – Пер. с англ. – СПб: Символ – Плюс, 2005. – 627 с., ил.

2. Котеров Д., Костарев А. PHP5 в подлиннике. – СПб: Символ – Плюс, 2005. – 1120 с., ил.

3. Дюбуа П. MySQL. Сборник рецептов. – Пер. с англ. - СПб: Символ – Плюс, 2004. – 1056 с., ил.

4. Томсон Лаура, Веллинг Люк. Разработка web – приложений на PHP и MySQL. – Пер. с англ. – СПб: ООО «ДиаСофтЮП», 2003. 672 с., ил.