на тему рефераты Информационно-образоательный портал
Рефераты, курсовые, дипломы, научные работы,
на тему рефераты
на тему рефераты
МЕНЮ|
на тему рефераты
поиск
Обработка ошибок в коде программ РНР
истинг3.2.Файл stack.php

<?php ## Инструкция try во вложенных функциях.

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

try {

echo "Начало try-блока.<br>";

outer();

echo "Конец try-блока.<br>";

} catch (Exception $e) {

echo " Исключение: {$e->getMessage()}<br>";

}

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

function outer() {

echo "Вошли в функцию ".__METHOD__."<br>";

inner();

echo "Вышли из функции ".__METHOD__."<br>";

}

function inner() {

echo "Вошли в функцию ".__METHOD__."<br>";

throw new Exception("Hello!");

echo "Вышли из функции ".__METHOD__."<br>";

}

?>

Результат работы данного кода выглядит так:

Начало программы.

Начало try-блока.

Вошли в функцию outer

Вошли в функцию inner

Исключение: Hello!

Конец программы.

Мы убеждаемся, что ни один из операторов echo, вызываемых после инструкции throw, не "сработал". По сути, программа даже не дошла до них: управление было мгновенно передано в catch-блок, а после этого -- в следующую за try...catch строку программы.

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

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

3.4 ИСКЛЮЧЕНИЯ И ДЕСТРУКТОРЫ

Деструктор любого объекта вызывается всякий раз, когда последняя ссылка на этот объект оказывается потерянной, например, программа выходит за границу области видимости переменной. Применительно к механизму обработки исключений это дает мощный инструмент -- корректное уничтожение всех объектов, созданных до вызова throw. Листинг 3.3 иллюстрирует ситуацию.

Листинг 3.3. Файл destr.php

<?php ## Деструкторы и исключения.

// Класс, комментирующий операции со своим объектом.

class Orator {

private $name;

function __construct($name) {

$this->name = $name;

echo "Создан объект {$this->name}.<br>";

}

function __destruct() {

echo "Уничтожен объект {$this->name}.<br>";

}

}

function outer() {

$obj = new Orator(__METHOD__);

inner();

}

function inner() {

$obj = new Orator(__METHOD__);

echo "Внимание, вбрасывание!<br>";

throw new Exception("Hello!");

}

// Основная программа.

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

try {

echo "Начало try-блока.<br>";

outer();

echo "Конец try-блока.<br>";

} catch (Exception $e) {

echo " Исключение: {$e->getMessage()}<br>";

}

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

?>

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

Результат работы программы выглядит так:

Начало программы.

Начало try-блока.

Создан объект outer.

Создан объект inner.

Внимание, вбрасывание!

Уничтожен объект inner.

Уничтожен объект outer.

Исключение: Hello!

Конец программы.

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

3.5 ИСКЛЮЧЕНИЯ И set_error_handler()

В п.2 рассматривали подход к обработке нефатальных ошибок, а именно установку функции-обработчика посредством вызова функции set_error_handler(). В РНР версии 4 он являлся единственно допустимым методом.

Функция-обработчик имеет один огромный недостаток: в ней неизвестно точно, что же следует предпринять в случае возникновения ошибки.

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

Листинг 3.4. Файл seh.php

<?php ## Недостатки set_error_handler().

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

set_error_handler("handler");

{

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

echo "Все, что имеет начало...<br>";

// Генерируем ("выбрасываем") исключение.

trigger_error("Hello!");

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

}

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

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

function handler($num, $str) {

// Код обработчика.

echo "Ошибка: $str<br>";

// exit();

}

?>

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

Начало программы.

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

Ошибка: Hello!

За счет использования exit () в функции handler()новая программа не только подвергает сомнению известный тезис (см. операторы echo), но также и утверждает, что любая, даже малейшая, ошибка является фатальной.

Что ж, раз проблема в команде exit(), попробуем ее убрать из скрипта и увидим следующий результат:

Начало программы.

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

Ошибка: Hello!

...имеет и конец.

Конец программы.

И снова мы получили не то, что нужно: ошибка теперь уже не является "чересчур фатальной", как раньше, у нее противоположная проблема: она, наоборот, недостаточно фатальна.

Мы-то хотели разрушать идиому о конечности всего, что имеет начало, а получили -- просто робкое замечание, произнесенное шепотом из-за кулис.

3.6 КЛАССИФИКАЦИЯ И НАСЛЕДОВАНИЕ

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

Листинг
3.5 иллюстрирует тот факт, что при перехвате исключений используется информация о наследовании классов-исключений.

Листинг 3.5. Файл inherit.php

<?php ## Наследование исключений.

// Исключение - ошибка файловых операций.

class FilesystemException extends Exception {

private $name;

public function __construct($name) {

parent::__construct($name);

$this->name = $name;

}

public function getName() { return $this->name; }

}

// Исключение - файл не найден.

class FileNotFoundException extends FilesystemException {}

// Исключение - Ошибка записи в файл.

class FileWriteException extends FilesystemException {}

try {

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

if (!file_exists("spoon"))

throw new FileNotFoundException("spoon");

} catch (FilesystemException $e) {

// Ловим ЛЮБОЕ файловое исключение!

echo "Ошибка при работе с файлом '{$e->getName()}'.<br>";

} catch (Exception $e) {

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

echo "Другое исключение: {$e->getDirName()}.<br>";

}

?>

В программе мы генерируем ошибку типа FileNotFoundException, однако, ниже перехватываем исключение не прямо этого класса, а его "родителя" -- FilesystemException. Так как любой объект типа FileNotFoundException является также и объектом класса FilesystemException, блок catch "срабатывает" для него. Кроме того, на всякий случай мы используем блок "поимки" объектов класса Exception -- "родоначальника" всех исключений. Если вдруг в программе произойдет исключение другого типа (обязательно производного от Exception), оно также будет обработано.

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

3.7 БАЗОВЫЙ КЛАСС Exception

РНР последних версий не допускает использования объектов произвольного типа в качестве исключений. Если вы создаете свой собственный класс-исключение, то должны унаследовать его от встроенного типа
Exception.

До сих пор мы пользовались только стандартным классом Exception, не определяя от него производных. Дело в том, что данный класс уже содержит довольно много полезных методов (например, getMessage ()), которые можно применять в программе.

Итак, каждый класс-исключение в листинге 3.5 наследует встроенный в РНР тип Exception. В этом типе есть много полезных методов и свойств, которые мы сейчас перечислим (приведен интерфейс класса):

class Exception {

protected $message; // текстовое сообщение

protected $code; // числовой код

protected $file; // имя файла, где создано исключение

protected $line; // номер строки, где создан объект

private $trace; // стек вызовов

public function__construct([string $message] [,int $code]);

public final function getMessageО; // возвращает $this->message

public final function getCode{); // возвращает $this->code

public final function getFileO; // возвращает $this->file

public final function getLine(); // возвращает $this->line

public final function getTrace();

public final function getTraceAsStringO;

public function __toStringO;

}

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

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

Конструктор класса принимает два необязательных аргумента, которые он записывает в соответствующие свойства объекта. Он также заполняет свойства $fiie, $line и $trace, соответственно, именем файла, номером строки и результатом вызова функции debug_backtrace() (информацию о функциях, вызвавших данную, см. в п. 2).

Стек вызовов, сохраненный в свойстве $trace, представляет собой список с именами функций (и информацией о них), которые вызвали текущую процедуру перед генерацией исключения. Данная информация полезна при отладке скрипта и может быть получена при помощи метода getTrace(). Дополнительный метод getTraceAsString() возвращает то же самое, но в строковом представлении.

Оператор преобразования в строку _toString() выдает всю информацию, сохраненную в объекте-исключении. При этом используются все свойства объекта, а также вызывается getTraceAsString() для преобразования стека вызовов в строку. Результат, который генерирует метод, довольно интересен (листинг.3.6).

Листинг 3.6. Файл tostring.php

<?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.

Страницы: 1, 2, 3, 4, 5, 6



© 2003-2013
Рефераты бесплатно, курсовые, рефераты биология, большая бибилиотека рефератов, дипломы, научные работы, рефераты право, рефераты, рефераты скачать, рефераты литература, курсовые работы, реферат, доклады, рефераты медицина, рефераты на тему, сочинения, реферат бесплатно, рефераты авиация, рефераты психология, рефераты математика, рефераты кулинария, рефераты логистика, рефераты анатомия, рефераты маркетинг, рефераты релиния, рефераты социология, рефераты менеджемент.