Обработка ошибок в коде программ РНР

Фаза "избавления" программы от ошибок. Задача обработки ошибок в коде программы. Ошибки с невозможностью автоматического восстановления, оператор отключения. Прекращение выполнения программы. Возврат недопустимого значения. Директивы РНР контроля ошибок.

Рубрика Программирование, компьютеры и кибернетика
Вид учебное пособие
Язык русский
Дата добавления 27.04.2009
Размер файла 62,3 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

<?php ## Вывод сведений об исключении.

function test($n) {

$e = new Exception("bang-bang #$n!");

echo "<pre>", $e, "</pre>";

}

function outer() { test(101); }

outer();

?>

Выводимый текст будет примерно следующим:

exception 'Exception' with message 'bang-bang #101!' in tostring.php:3

Stack trace:

#0 tostring.php(6): test(101)

#1 tostring.php{7): outer()

#2 (main)

3.8 ИСПОЛЬЗОВАНИЕ ИНТЕРФЕЙСОВ

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

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

Использование интерфейсов вместе с исключениями возможно, начиная с РНР 5.0.1.

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

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

* файловые (ошибка открытия, чтения или записи в файл);

* сетевые (например, невозможность соединения с сервером);

? пользовательские: сообщения выдаются прямо в браузер.

Для классификации сущностей в программе удобно использовать интерфейсы. Давайте так и поступим по отношению к объектам-исключениям (листинг 3.7).

Листинг 3.7. Файл iface/interfaces.php

<?php ## Классификация исключений.

interface IException {}

interface IInternalException extends IException {}

interface IFileException extends IInternalException {}

interface INetException extends IInternalException {}

interface IUserException extends IException {}

?>

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

Теперь, если в программе имеется некоторый объект-исключение, чей класс реализует интерфейс INetException, мы также сможем убедиться, что он реализует и интерфейс IInternalException:

if ($obj instanceof IlnternalException) echo "Это внутренняя ошибка.";

Кроме того, если мы будем использовать конструкцию catch (InternalException ...), то сможем перехватить любое из исключений, реализующих интерфейсы IFileException и INetException.

Мы также "на всякий случай" задаем одного общего предка у всех интерфейсов -- lException. Вообще говоря, это делать не обязательно.

Интерфейсы, конечно, не могут существовать сами по себе, и мы не можем создавать объекты типов IFileException (к примеру) напрямую. Необходимо определить классы, которые будут реализовывать наши интерфейсы (листинг 3.8).

Листинг 3.8. Файл iface/exceptions.php

<?php ## Классы-исключения.

require_once "interfaces.php";

// Ошибка: файл не найден.

class FileNotFoundException extends Exception

implements IFileException {}

// Ошибка: ошибка доступа к сокету.

class SocketException extends Exception

implements INetException {}

// Ошибка: неправильный пароль пользователя.

class WrongPassException extends Exception

implements IUserException {}

// Ошибка: невозможно записать данные на сетевой принтер.

class NetPrinterWriteException extends Exception

implements IFileException, INetException {}

// Ошибка: невозможно соединиться с SQL-сервером.

class SqlConnectException extends Exception

implements IInternalException, IUserException {}

?>

Обратите внимание на то, что исключение типа NetPrinterWriteException реализует сразу два интерфейса. Таким образом, оно может одновременно трактоваться и как файловое, и как сетевое исключение, и перехватываться как конструкцией catch (IFileException ...), так и catch (InetException ...).

За счет того, что все классы-исключения обязательно должны наследовать базовый тип Exception, мы можем, как обычно, проверить, является ли переменная объектом-исключением, или она имеет какой-то другой тип:

if ($obj instanceof Exception) echo "Это объект-исключение.";

Рассмотрим теперь пример кода, который использует приведенные выше классы (листинг3.9).

Листинг 3.9. Файл iface/test.php

<?php ## Использование иерархии исключений.

require_once "exceptions.php";

try {

printDocument();

} catch (IFileException $e) {

// Перехватываем только файловые исключения.

echo "Файловая ошибка: {$e->getMessage()}.<br>";

} catch (Exception $e) {

// Перехват всех остальных исключений.

echo "Неизвестное исключение: <pre>", $e, "</pre>";

}

function printDocument() {

$printer = "//./printer";

// Генерируем исключение типов IFileException и INetException.

if (!file_exists($printer))

throw new NetPrinterWriteException($printer);

}

?>

Результатом работы этой программы (в случае ошибки) будет строчка:

Ошибка записи в файл //./printer.

3.9 БЛОКИ-ФИНАЛИЗАТОРЫ

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

3.9.1 Неподдерживаемая конструкция try...finally

В языках программирования Java и Delphi для реализации кода-финализатора имеется очень удобная конструкция try...finally, призванная гарантировать выполнение некоторых действий в случае возникновения исключения или внезапного завершения функции по return. На РНР это можно было бы записать так:

function eatThis() { throw new Exception("bang-bang!"); } function hello() {

echo "Все, что имеет начало, ";

try {

eatThis () ;

} finally {

echo "имеет и конец.";

}

echo "this never prints!"; }

// Вызываем функцию, hello() ;

Семантика инструкции try...finally должна быть ясна: она гарантирует выполнение finally-блока, даже если внезапно будет осуществлен выход из try-блока.

К сожалению, Zend Engine 2, на которой построен РНР 5, пока не поддерживает конструкцию try...finally, так что приведенный выше код, скорее всего, откажется работать. Почему "скорее всего"? Да потому, что есть все основания полагать, что рано или поздно инструкция finally в РНР появится, поскольку она очень удобна. Возможно, что инструкция finally уже появилась.

3.9.2 "Выделение ресурса есть инициализация"

Как же быть в случае, если нам нужно написать код, который будет обязательно выполнен при завершении работы функции? Единственная на данный момент возможность добиться этого -- помещение такого кода в деструктор некоторого класса и создание объекта этого класса непосредственно в функции. Мы знаем, что при выходе из процедуры РНР автоматически уничтожает все переменные-ссылки, созданные внутри тела процедуры. Соответственно, если ссылка на объект будет един-ственной, то вызовется деструктор его класса. В листинге 3.3 мы уже рассматривали такой подход.

В соответствии с терминологией Страуструпа данный подход называют "выделение ресурса есть инициализация". Это объясняется вот чем: обычно в finally-блоках программы производится "освобождение" некоторых объектов-ресурсов, "выделенных" до момента возникновения исключения. Вызов конструктора объекта -- это его инициализация.

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

3.9.3 Перехват всех исключений

Поскольку любой класс-исключение произволен от класса Exception, мы можем написать один-единственный блок-обработчик для всех возможных исключений в программе:

echo "Начало программы.<br>";

try {

eatThis ();

}

catch (Exception $e)

{

echo "Неперехваченное исключение: ", $e;

}

echo "Конец программы.<br>";

Таким образом, если в функции eatThis() возникнет любая исключительная ситуация, и объект-исключение "выйдет" за ее пределы (т. е. не будет перехвачен внутри самой процедуры), сработает наш универсальный код восстановления (оператор echo).

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

К сожалению, неожиданные вызовы return в функции при этом не обрабатываются, и отследить их пока нельзя.

Рассмотрим пример функции, которую мы пытались написать выше с использованием try...finally. Фактически, листинг 3.10 иллюстрирует, как можно проэмулировать finally в программе на РНР.

Листинг 3.10. Файл catchall.php

<?php ## Перехват всех исключений.

// Пользовательское исключение.

class HeadshotException extends Exception {}

// Функция, генерирующая исключение.

function eatThis() { throw new HeadshotException("bang-bang!"); }

// Функция с кодом-финализатором.

function action() {

echo "Все, что имеет начало, ";

try {

// Внимание, опасный момент!

eatThis();

} catch (Exception $e) {

// Ловим ЛЮБОЕ исключение, выводим текст...

echo "имеет и конец.<br>";

// ...а потом передаем это исключение дальше.

throw $e;

}

}

try {

// Вызываем функцию.

action();

} catch (HeadshotException $e) {

echo "Извините, вы застрелились: {$e->getMessage()}";

}

?>

В результате работы программы в браузере будет выведен следующий текст:

Все, что имеет начало, имеет и конец.

Извините, вы застрелились: bang-bang!

Как видите, код-финализатор в функции action() срабатывает "прозрачно" для вызывающей программы: исключение типа HeadsnotException не теряется, а выходит за пределы функции за счет повторного использования throw внутри catch-блока.

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

3.10 ТРАНСФОРМАЦИЯ ОШИБОК

Мы разделили все ошибки на два вида:

? "несерьезные" - диагностические сообщения; перехватываются при помощи set_error_handier();

? "серьезные" - невозможно продолжить нормальный ход работы кода, представлены исключениями.

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

Известно, что в программировании любая ошибка может быть усилена, по крайней мере, без ухудшения качества кода. Например, если заставить РНР немедленно завершать работу скрипта не только при обнаружении ошибок класса E_ERROR и E_PARSE (перехват которых вообще невозможен), но также и при возникновении E_WARNING и даже E_NOTICE, программа станет более "хрупкой" к неточностям во входных данных. Но зато программист будет просто вынужден волей-неволей писать более качественный код, проверяющий каждую мелочь при своей работе. Таким образом, качество написания кода при "ужесточении" реакции на ошибку способно только возрасти, а это обычно является большим достоинством.

3.10.1 Серьезность "несерьезных" ошибок

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

Для примера рассмотрим сообщение класса E_WARNING, возникающее при ошибке открытия файла. Является ли оно фатальным, и возможно ли дальнейшее выполнение программы при его возникновении без каких-либо ветвлений? Однозначного ответа на этот вопрос дать нельзя.

Вот две крайние ситуации.

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

? Невозможность открытия файла практически ни на что не влияет. К примеру, программа может записывать в этот файл информацию о том, когда она была запущена. Или даже более простой пример: скрипт просто проверяет, существует ли нужный файл, а если его нет, то создает новый пустой.

Рассмотрим теперь самое "слабое" сообщение, класса E_NOTICE, которое генерируется РНР, например, при использовании неинициализированной переменной. Часто такие ошибки считают настолько незначительными, что даже отключают реакцию на них в файле php.ini (error_reporting=E_ALL~E_NOTICE). Более того, именно такое значение error_reporting выставляется по умолчанию в дистрибутиве PHP.

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

Предположим, вы исполняете SQL-запрос для добавления новой записи в таблицу MySQL:

INSERT INTO table (id, parent_id, text)

VALUES (NULL, '$pid', 'Have you ever had a dream, that you were so sure was real?')

В переменной $pid хранится некоторый идентификатор, который должен быть обязательно числовым. Если эта переменная окажется неинициализированной (например, где-то в программе выше произошла опечатка), будет сгенерирована ошибка E_NOTICE, а вместо $pid подставится пустая строка. SQL-запрос же все равно останется синтаксически корректным. В результате в базе данных появится запись с полем parent_id, равным нулю (пустая строка '' без всяких предупреждений трактуется MySQL как 0). Это значение может быть недопустимым для поля parent_id (например, если оно является внешним ключом для таблицы table, т. е. указывает на другую "родительскую" запись с определенным ID). А раз значение недопустимо, то целостность базы данных нарушена, и это в дальнейшем вполне может привести к серьезным последствиям (заранее непредсказуемым) в других частях скрипта, причем об их связи с одним-единственным E_NOTICE, сгенерированным ранее, останется только догадываться.

? Теперь о том, когда E_NOTICE может быть безвредной. Вот пример кода:

cinput type="text" name "field"

value="<?=htmlspecialchars($_REQUEST['field'])?>">

Очевидно, что если ячейка $_REQUEST['field'] не была инициализирована (например, скрипт вызван путем набора его адреса в браузере и не принимает никаких входных данных), элемент формы должен быть пуст. Подобная ситуация настолько широко распространена, что обычно ставят @ перед обращением к элементу массива, или даже перед htmlspecialchars(). В этом случае сообщение будет точно подавлено.

3.10.2 Преобразование ошибок в исключения

Мы приходим к выводу, что ошибку любого уровня можно трактовать как "серьезную" (за исключением ситуации, когда перед выражением явно указан оператор @, подавляющий вывод всех ошибок. Для обработки же серьезных ошибок в РНР имеется прекрасное средство -- исключения.

Пример. Решение, которое мы здесь рассмотрим, -- библиотека для автоматического преобразования всех перехватываемых ошибок РНР (вроде E_WARNING, E_NOTICE и т. д.) в объекты-исключения одноименных классов. Таким образом, если программа не сможет, например, открыть какой-то файл, теперь будет сгенерировано исключение, которое можно перехватить в соответствующем участке программы. Листинг 3.11 иллюстрирует сказанное.

Листинг 3.11. Файл w2e_simple.php

<?php ## Преобразование ошибок в исключения.

require_once "lib/config.php";

require_once "PHP/Exceptionizer.php";

// Для большей наглядности поместим основной проверочный код в функцию.

suffer();

// Убеждаемся, что перехват действительно был отключен.

echo "<b>Дальше должно идти обычное сообщение PHP.</b>";

fopen("fork", "r");

function suffer() {

// Создаем новый объект-преобразователь. Начиная с этого момента

// и до уничтожения переменной $w2e все перехватываемые ошибки

// превращаются в одноименные исключения.

$w2e = new PHP_Exceptionizer(E_ALL);

try {

// Открываем несуществующий файл. Здесь будет ошибка E_WARNING.

fopen("spoon", "r");

} catch (E_WARNING $e) {

// Перехватываем исключение класса E_WARNING.

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

}

// В конце можно явно удалить преобразователь командой:

// unset($w2e);

// Но можно этого и не делать - переменная и так удалится при

// выходе из функции (при этом вызовется деструктор объекта $w2e,

// отключающий слежение за ошибками).

}

?>

Обратите внимание на заголовок catch-блока. Он может поначалу ввести в заблуждение: ведь перехватывать можно только объекты-исключения, указывая имя класса, но никак не числовое значение (E_WARNING -- вообще говоря, константа РНР, числовое значение которой равно 2 -- можете убедиться в этом, запустив оператор echo E_WARNING). Тем не менее ошибки нет: E_WARNING -- это одновременно и имя класса, определяемого в библиотеке PHP_Exceptionizer.

Заметьте также, что для ограничения области работы перехватчика используется уже знакомая нам идеология: "выделение ресурса есть инициализация". А именно в том месте, с которого необходимо начать преобразование, мы помещаем оператор создания нового объекта PHP_Exceptionizer и запоминаем последний в переменной, а там, где преобразование следует закончить, просто уничтожаем объект-перехватчик (явно или, как в примере, неявно, при выходе из функции).

3.10.3 Код библиотеки PHP_Exceptionizer

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

Листинг 3.12. Файл lib/PHP/Exceptionizer.php

<?php ## Класс для преобразования ошибок PHP в исключения.

/**

* Класс для преобразования перехватываемых (см. set_error_handler())

* ошибок и предупреждений PHP в исключения.

*

* Следующие типы ошибок, хотя и поддерживаются формально, не могут

* быть перехвачены:

* E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR,

* E_COMPILE_WARNING

*/

class PHP_Exceptionizer {

// Создает новый объект-перехватчик и подключает его к стеку

// обработчиков ошибок PHP (используется идеология "выделение

// ресурса есть инициализация").

public function __construct($mask=E_ALL, $ignoreOther=false) {

$catcher = new PHP_Exceptionizer_Catcher();

$catcher->mask = $mask;

$catcher->ignoreOther = $ignoreOther;

$catcher->prevHdl = set_error_handler(array($catcher, "handler"));

}

// Вызывается при уничтожении объекта-перехватчика (например,

// при выходе его из области видимости функции). Восстанавливает

// предыдущий обработчик ошибок.

public function __destruct() {

restore_error_handler();

}

}

/**

* Внутренний класс, содержащий метод перехвата ошибок.

* Мы не можем использовать для этой же цели непосредственно $this

* (класса PHP_Exceptionizer): вызов set_error_handler() увеличивает

* счетчик ссылок на объект, а он должен остаться неизменным, чтобы в

* программе всегда оставалась ровно одна ссылка.

*/

class PHP_Exceptionizer_Catcher {

// Битовые флаги предупреждений, которые будут перехватываться.

public $mask = E_ALL;

// Признак, нужно ли игнорировать остальные типы ошибок, или же

// следует использовать стандартный механизм обработки PHP.

public $ignoreOther = false;

// Предыдущий обработчик ошибок.

public $prevHdl = null;

// Функция-обработчик ошибок PHP.

public function handler($errno, $errstr, $errfile, $errline) {

// Если error_reporting нулевой, значит, использован оператор @,

// и все ошибки должны игнорироваться.

if (!error_reporting()) return;

// Перехватчик НЕ должен обрабатывать этот тип ошибки?

if (!($errno & $this->mask)) {

// Если ошибку НЕ следует игнорировать...

if (!$this->ignoreOther) {

if ($this->prevHdl) {

// Если предыдущий обработчик существует, вызываем его.

$args = func_get_args();

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

} else {

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

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

return false;

}

}

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

return true;

}

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

$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>\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 с., ил.


Подобные документы

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

    курсовая работа [1,2 M], добавлен 19.02.2015

  • Сущность метода перестановочного декодирования. Особенности использования метода вылавливания ошибок. Декодирование циклического кода путем вылавливания ошибок. Распознавание пакетов ошибок как особенность циклических кодов. Вычисление вектора ошибок.

    доклад [20,3 K], добавлен 24.05.2012

  • Порядок оценки точности системы автоматического управления по величине установившейся ошибки при типовых воздействиях, механизм ее повышения. Разновидности ошибок и методика их вычисления. Определение ошибок по виду частотных характеристик системы.

    реферат [103,3 K], добавлен 11.08.2009

  • Решение задач синтаксического анализа простой программы. Алгоритм нахождения синтаксических ошибок в ее тексте. Вывод данных о местоположении ошибки. Проектирование программы анализа арифметического выражения и методы проверки его на сумму или разность.

    курсовая работа [2,6 M], добавлен 01.07.2011

  • Организация вычислительных процессов и программирования на алгоритмическом языке. Создание программы "Калькулятор". Выбор языка и среды программирования. Функциональная схема работы программы, описание разработанных алгоритмов. Способы устранения ошибок.

    курсовая работа [434,1 K], добавлен 27.08.2012

  • Обоснование необходимости разработки виртуального магазина. Описание форм программы. Требования к аппаратному и программному обеспечению. Тестирование и выявление ошибок. Область применения программы. Расчет экономического эффекта проекта. Охрана труда.

    дипломная работа [1,7 M], добавлен 20.12.2012

  • Словесное описание алгоритма программы. Открытие файла процедурой Rewrite, его проверка на наличие ошибок при открытии. Особенности построения диаграммы. Листинг программы, ее тестирование и отладка. Выполнение процедуры CloseFile при закрытии файла.

    контрольная работа [17,3 K], добавлен 11.06.2010

  • Технология разработки и внедрения программного обеспечения автоматизированной системы управления. Классификация ошибок в программах на этапе эксплуатации системы и общие задачи процесса ее отладки. Методы обнаружениея и локализации ошибок в программах.

    контрольная работа [480,4 K], добавлен 25.10.2010

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

    реферат [192,2 K], добавлен 10.11.2014

  • Характеристика используемой операционной системы, языка программирования. Структура программы на языке Turbo Pascal 7.1. Операторы языка Turbo Pascal. Проведение сортировки записей. Алгоритмы программы и подпрограмм. Причины возникновения ошибок.

    курсовая работа [454,1 K], добавлен 13.06.2014

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.